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

Bitbucket commits-noreply at bitbucket.org
Mon Jul 18 09:03:44 UTC 2011


2 new changesets in ckan:

http://bitbucket.org/okfn/ckan/changeset/d7fb622e6fdb/
changeset:   d7fb622e6fdb
branch:      feature-1141-moderated-edits-ajax
user:        John Glover
date:        2011-07-18 10:57:02
summary:     [moderaetdedits] Bug fix: set pkg object in context variable so that the tab bar is created on the Authorization page
affected #:  1 file (79 bytes)

--- a/ckan/controllers/package.py	Thu Jul 14 19:28:02 2011 +0100
+++ b/ckan/controllers/package.py	Mon Jul 18 09:57:02 2011 +0100
@@ -502,6 +502,7 @@
         pkg = model.Package.get(id)
         if pkg is None:
             abort(404, gettext('Package not found'))
+        c.pkg = pkg # needed to add in the tab bar to the top of the auth page
         c.pkgname = pkg.name
         c.pkgtitle = pkg.title
 


http://bitbucket.org/okfn/ckan/changeset/1ae0aa75e6e5/
changeset:   1ae0aa75e6e5
branch:      feature-1141-moderated-edits-ajax
user:        John Glover
date:        2011-07-18 10:57:42
summary:     [moderatededits] Merge with default
affected #:  52 files (37.5 KB)

--- a/.hgtags	Mon Jul 18 09:57:02 2011 +0100
+++ b/.hgtags	Mon Jul 18 09:57:42 2011 +0100
@@ -31,3 +31,4 @@
 e19182e8f8db1a8058ba7872b1808aa73764de4f ckan-1.3.3
 d83e20d807a6d5587ae5adef49e31fe48c906f9a ckan-1.3.3
 3a59aa5b63d06dde77424e3313433a1fb9eb1215 ckan-1.4
+a394ca150daac6977114ce5d8c911f7d9896d1cd ckan-1.4.1


--- a/CHANGELOG.txt	Mon Jul 18 09:57:02 2011 +0100
+++ b/CHANGELOG.txt	Mon Jul 18 09:57:42 2011 +0100
@@ -1,18 +1,28 @@
 CKAN CHANGELOG
 ++++++++++++++
 
-v1.4.1 2011-XX-XX
+v1.4.2 2011-XX-XX
 =================
+Major:
+  * Packages revisions can be marked as 'moderated' (#1141)
+  * Viewing of a package at any revision (#1141)
+
+
+v1.4.1 2011-06-27
+=================
+Major:
+  * Refactor Web interface to use logic layer rather than model objects directly (#1078)
+
 Minor:
   * Links in user-supplied text made less attractive to spammers (nofollow) #1181 
   * Package change notifications - remove duplicates (#1149)
   * Metadata dump linked to (#1169)
   * Refactor authorization code to be common across Package, Group and Authorization Group (#1074)
-  * Refactor Web interface to use logic layer rather than model objects directly (#1078)
 
 Bug fixes
   * Duplicate authorization roles were difficult to delete (#1083)
 
+
 v1.4 2011-05-19
 ===============
 Major:


--- a/README.txt	Mon Jul 18 09:57:02 2011 +0100
+++ b/README.txt	Mon Jul 18 09:57:42 2011 +0100
@@ -358,6 +358,19 @@
 
    A common error when wanting to run tests against a particular database is to change the sqlalchemy.url in test.ini or test-core.ini. The problem is that these are versioned files and people have checked in these by mistake, creating problems for all other developers and the buildbot. This is easily avoided by only changing the sqlalchemy.url in your local development.ini and testing --with-pylons=test-core.ini.
 
+Common problems running tests
+-----------------------------
+
+* `nose.config.ConfigError: Error reading config file 'setup.cfg': no such option 'with-pylons'`
+
+   This error can result when you run nosetests for two reasons:
+
+   1. Pylons nose plugin failed to run. If this is the case, then within a couple of lines of running `nosetests` you'll see this warning: `Unable to load plugin pylons` followed by an error message. Fix the error here first.
+
+   2. The Python module 'Pylons' is not installed into you Python environment. Confirm this with::
+
+        python -c "import pylons"
+
 Testing extensions
 ------------------
 


--- a/ckan/__init__.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/__init__.py	Mon Jul 18 09:57:42 2011 +0100
@@ -1,4 +1,4 @@
-__version__ = '1.4.2a'
+__version__ = '1.4.3a'
 __description__ = 'Comprehensive Knowledge Archive Network (CKAN) Software'
 __long_description__ = \
 '''The CKAN software is used to run the Comprehensive Knowledge Archive


--- a/ckan/config/routing.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/config/routing.py	Mon Jul 18 09:57:42 2011 +0100
@@ -230,10 +230,12 @@
     # Note: openid users have slashes in their ids, so need the wildcard
     # in the route.
     map.connect('/user/edit/{id:.*}', controller='user', action='edit')
+    map.connect('/user/reset/{id:.*}', controller='user', action='perform_reset')
     map.connect('/user/register', controller='user', action='register')
     map.connect('/user/login', controller='user', action='login')
     map.connect('/user/logged_in', controller='user', action='logged_in')
     map.connect('/user/logged_out', controller='user', action='logged_out')
+    map.connect('/user/reset', controller='user', action='request_reset')
     map.connect('/user/me', controller='user', action='me')
     map.connect('/user/{id:.*}', controller='user', action='read')
     map.connect('/user', controller='user', action='index')


--- a/ckan/controllers/group_formalchemy.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/controllers/group_formalchemy.py	Mon Jul 18 09:57:42 2011 +0100
@@ -65,7 +65,7 @@
             for extension in self.extensions:
                 extension.create(group)
             model.repo.commit_and_remove()
-            h.redirect_to(action='read', id=c.groupname)
+            h.redirect_to(controller='group', action='read', id=c.groupname)
 
         if request.params:
             data = forms.edit_group_dict(ckan.forms.get_group_dict(), request.params)
@@ -122,4 +122,4 @@
             for extension in self.extensions: 
                 extension.edit(group)
             model.repo.commit_and_remove()
-            h.redirect_to(action='read', id=c.groupname)
+            h.redirect_to(controller='group', action='read', id=c.groupname)


--- a/ckan/controllers/package_formalchemy.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/controllers/package_formalchemy.py	Mon Jul 18 09:57:42 2011 +0100
@@ -66,8 +66,11 @@
                 if domain.startswith('www.'):
                     domain = domain[4:]
             # ensure all fields specified in params (formalchemy needs this on bind)
-            data = ckan.forms.add_to_package_dict(ckan.forms.get_package_dict(fs=fs), request.params)
+            from ckan.forms import add_to_package_dict,get_package_dict
+
+            data = add_to_package_dict(get_package_dict(fs=fs), request.params)
             fs = fs.bind(model.Package, data=data, session=model.Session)
+
         else:
             fs = fs.bind(session=model.Session)
         #if 'preview' in request.params:


--- a/ckan/controllers/user.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/controllers/user.py	Mon Jul 18 09:57:42 2011 +0100
@@ -1,4 +1,3 @@
-import re
 import logging
 
 import genshi
@@ -7,6 +6,7 @@
 
 import ckan.misc
 from ckan.lib.base import *
+from ckan.lib import mailer
 
 log = logging.getLogger(__name__)
 
@@ -163,8 +163,7 @@
                     c.user_fullname = request.params.getone('fullname')
                     c.user_email = request.params.getone('email')
                     return render('user/edit.html')
-
-                user.about = request.params.getone('about')
+                user.about = about
                 user.fullname = request.params.getone('fullname')
                 user.email = request.params.getone('email')
                 try:
@@ -184,7 +183,40 @@
             h.redirect_to(controller='user', action='read', id=user.id)
             
         return render('user/edit.html')
-        
+    
+    def request_reset(self):
+        if request.method == 'POST':
+            id = request.params.get('user')
+            user = model.User.get(id)
+            if user is None:
+                h.flash_error(_('No such user: %s') % id)
+            try:
+                mailer.send_reset_link(user)
+                h.flash_success(_('Please check your inbox for a reset code.'))
+                redirect('/')
+            except mailer.MailerException, e:
+                h.flash_error(_('Could not send reset link: %s') % unicode(e))
+        return render('user/request_reset.html')
+
+    def perform_reset(self, id):
+        user = model.User.get(id)
+        if user is None:
+            abort(404)
+        c.reset_key = request.params.get('key')
+        if not mailer.verify_reset_link(user, c.reset_key):
+            h.flash_error(_('Invalid reset key. Please try again.'))
+            abort(403)
+        if request.method == 'POST':
+            try:
+                user.password = self._get_form_password()
+                model.Session.add(user)
+                model.Session.commit()
+                h.flash_success(_("Your password has been reset."))
+                redirect('/')
+            except ValueError, ve:
+                h.flash_error(unicode(ve))
+        return render('user/perform_reset.html')
+
     def _format_about(self, about):
         about_formatted = ckan.misc.MarkdownFormat().to_html(about)
         try:


--- a/ckan/i18n/ckan.pot	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/i18n/ckan.pot	Mon Jul 18 09:57:42 2011 +0100
@@ -6,9 +6,9 @@
 #, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: ckan 1.4\n"
+"Project-Id-Version: ckan 1.4.1\n"
 "Report-Msgid-Bugs-To: EMAIL at ADDRESS\n"
-"POT-Creation-Date: 2011-05-19 15:45+0100\n"
+"POT-Creation-Date: 2011-06-27 10:54+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -17,15 +17,11 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 0.9.5\n"
 
-#: ckan/misc.py:47
-msgid "<strong>Warning:</strong>: Text could not be rendered."
-msgstr ""
-
 #: ckan/controllers/api.py:40 ckan/controllers/authorization_group.py:19
-#: ckan/controllers/group.py:22 ckan/controllers/home.py:23
-#: ckan/controllers/package.py:45 ckan/controllers/revision.py:22
-#: ckan/controllers/tag.py:17 ckan/controllers/user.py:18
-#: ckan/controllers/user.py:49 ckan/controllers/user.py:75
+#: ckan/controllers/group.py:50 ckan/controllers/home.py:23
+#: ckan/controllers/package.py:95 ckan/controllers/revision.py:22
+#: ckan/controllers/tag.py:17 ckan/controllers/user.py:22
+#: ckan/controllers/user.py:53 ckan/controllers/user.py:79
 msgid "Not authorized to see this page"
 msgstr ""
 
@@ -47,133 +43,124 @@
 msgid "Cannot read entity of this type: %s"
 msgstr ""
 
-#: ckan/controllers/api.py:206 ckan/controllers/api.py:252
+#: ckan/controllers/api.py:209 ckan/controllers/api.py:255
 #, python-format
 msgid "JSON Error: %s"
 msgstr ""
 
-#: ckan/controllers/api.py:213
+#: ckan/controllers/api.py:216
 #, python-format
 msgid "Cannot create new entity of this type: %s %s"
 msgstr ""
 
-#: ckan/controllers/api.py:229 ckan/controllers/api.py:273
+#: ckan/controllers/api.py:232 ckan/controllers/api.py:277
+#: ckan/controllers/group.py:161 ckan/controllers/group.py:179
+#: ckan/controllers/package.py:370 ckan/controllers/package.py:397
 msgid "Integrity Error"
 msgstr ""
 
-#: ckan/controllers/api.py:258
+#: ckan/controllers/api.py:261
 #, python-format
 msgid "Cannot update entity of this type: %s"
 msgstr ""
 
-#: ckan/controllers/api.py:294
+#: ckan/controllers/api.py:298
 #, python-format
 msgid "Cannot delete entity of this type: %s %s"
 msgstr ""
 
-#: ckan/controllers/api.py:316
+#: ckan/controllers/api.py:321
 #, python-format
 msgid "There is no revision with id: %s"
 msgstr ""
 
-#: ckan/controllers/api.py:327
+#: ckan/controllers/api.py:332
 msgid "Missing search term ('since_id=UUID' or 'since_time=TIMESTAMP')"
 msgstr ""
 
-#: ckan/controllers/api.py:335
+#: ckan/controllers/api.py:340
 #, python-format
 msgid "Could not read parameters: %r"
 msgstr ""
 
-#: ckan/controllers/api.py:372
+#: ckan/controllers/api.py:377
 #, python-format
 msgid "Bad search option: %s"
 msgstr ""
 
-#: ckan/controllers/api.py:375
+#: ckan/controllers/api.py:380
 #, python-format
 msgid "Unknown register: %s"
 msgstr ""
 
-#: ckan/controllers/api.py:383
+#: ckan/controllers/api.py:388
 msgid "Malformed qjson value"
 msgstr ""
 
-#: ckan/controllers/api.py:392 ckan/lib/base.py:159
+#: ckan/controllers/api.py:397 ckan/lib/base.py:159
 msgid "Request params must be in form of a json encoded dictionary."
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:37 ckan/controllers/group.py:41
+#: ckan/controllers/authorization_group.py:37 ckan/controllers/group.py:69
 #, python-format
 msgid "Not authorized to read %s"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:55 ckan/controllers/group.py:65
+#: ckan/controllers/authorization_group.py:55 ckan/controllers/group.py:99
+#: ckan/controllers/group_formalchemy.py:29
 msgid "Unauthorized to create a group"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:103 ckan/controllers/group.py:119
-#: ckan/controllers/group.py:268
+#: ckan/controllers/authorization_group.py:103 ckan/controllers/group.py:421
+#: ckan/controllers/group_formalchemy.py:83
 #, python-format
 msgid "User %r not authorized to edit %r"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:147 ckan/controllers/group.py:166
-#: ckan/controllers/group.py:266
+#: ckan/controllers/authorization_group.py:147 ckan/controllers/group.py:188
+#: ckan/controllers/group.py:419
 msgid "Group not found"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:153 ckan/controllers/group.py:173
-msgid "Not authorized to edit authorization for group"
+#: ckan/controllers/authorization_group.py:155 ckan/controllers/group.py:194
+#: ckan/controllers/package.py:438
+#, python-format
+msgid "User %r not authorized to edit %s authorizations"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:173 ckan/controllers/group.py:191
-#: ckan/controllers/package.py:444
-msgid "Please select either a user or an authorization group, not both."
+#: ckan/controllers/group.py:134 ckan/controllers/group.py:157
+#: ckan/controllers/group.py:175
+#, python-format
+msgid "Unauthorized to read group %s"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:185 ckan/controllers/group.py:205
-#: ckan/controllers/package.py:457
+#: ckan/controllers/group.py:140 ckan/controllers/package.py:341
+#: ckan/controllers/package_formalchemy.py:97
 #, python-format
-msgid "Added role '%s' for user '%s'"
+msgid "User %r not authorized to edit %s"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:199 ckan/controllers/group.py:221
-#: ckan/controllers/package.py:473
-#, python-format
-msgid "Added role '%s' for authorization group '%s'"
+#: ckan/controllers/group.py:159 ckan/controllers/group.py:177
+#: ckan/controllers/package.py:178 ckan/controllers/package.py:214
+#: ckan/controllers/package.py:249 ckan/controllers/package.py:335
+#: ckan/controllers/package.py:368 ckan/controllers/package.py:395
+#: ckan/controllers/package.py:432
+msgid "Package not found"
 msgstr ""
 
-#: ckan/controllers/authorization_group.py:206 ckan/controllers/group.py:228
-#: ckan/controllers/package.py:480
-msgid "Error: No role found with that id"
-msgstr ""
-
-#: ckan/controllers/authorization_group.py:210 ckan/controllers/group.py:234
-#: ckan/controllers/package.py:485
-#, python-format
-msgid "Deleted role '%s' for user '%s'"
-msgstr ""
-
-#: ckan/controllers/authorization_group.py:213 ckan/controllers/group.py:237
-#: ckan/controllers/package.py:488
-#, python-format
-msgid "Deleted role '%s' for authorization group '%s'"
-msgstr ""
-
-#: ckan/controllers/group.py:259 ckan/controllers/package.py:193
+#: ckan/controllers/group.py:412 ckan/controllers/package.py:242
 msgid "Select two revisions before doing the comparison."
 msgstr ""
 
-#: ckan/controllers/group.py:275
+#: ckan/controllers/group.py:428
 msgid "CKAN Group Revision History"
 msgstr ""
 
-#: ckan/controllers/group.py:277
+#: ckan/controllers/group.py:430
 msgid "Recent changes to CKAN Group: "
 msgstr ""
 
-#: ckan/controllers/group.py:297 ckan/controllers/package.py:227
+#: ckan/controllers/group.py:450 ckan/controllers/package.py:276
 msgid "Log message: "
 msgstr ""
 
@@ -189,43 +176,30 @@
 msgid "No language given!"
 msgstr ""
 
-#: ckan/controllers/package.py:128 ckan/controllers/package.py:165
-#: ckan/controllers/package.py:200 ckan/controllers/package.py:420
-msgid "Package not found"
-msgstr ""
-
-#: ckan/controllers/package.py:151 ckan/controllers/package.py:173
+#: ckan/controllers/package.py:201 ckan/controllers/package.py:222
+#: ckan/controllers/package.py:333 ckan/controllers/package.py:366
+#: ckan/controllers/package.py:393
 #, python-format
 msgid "Unauthorized to read package %s"
 msgstr ""
 
-#: ckan/controllers/package.py:206
+#: ckan/controllers/package.py:255
 msgid "CKAN Package Revision History"
 msgstr ""
 
-#: ckan/controllers/package.py:208
+#: ckan/controllers/package.py:257
 msgid "Recent changes to CKAN Package: "
 msgstr ""
 
-#: ckan/controllers/package.py:251
+#: ckan/controllers/package.py:301 ckan/controllers/package_formalchemy.py:21
 msgid "Unauthorized to create a package"
 msgstr ""
 
-#: ckan/controllers/package.py:327
-#, python-format
-msgid "User %r not authorized to edit %s"
+#: ckan/controllers/package.py:659
+msgid "Package Not Found"
 msgstr ""
 
-#: ckan/controllers/package.py:426
-#, python-format
-msgid "User %r not authorized to edit %s authorizations"
-msgstr ""
-
-#: ckan/controllers/package.py:505
-msgid "404 Package Not Found"
-msgstr ""
-
-#: ckan/controllers/package.py:512
+#: ckan/controllers/package.py:666
 msgid "Rating value invalid"
 msgstr ""
 
@@ -246,108 +220,131 @@
 msgid "Revision updated"
 msgstr ""
 
-#: ckan/controllers/tag.py:43 ckan/forms/common.py:919
+#: ckan/controllers/tag.py:43 ckan/forms/common.py:922
 msgid "Other"
 msgstr ""
 
-#: ckan/controllers/user.py:81
-msgid "That username is not available."
+#: ckan/controllers/user.py:86
+#, python-format
+msgid "Missing parameter: %r"
 msgstr ""
 
-#: ckan/controllers/user.py:105
+#: ckan/controllers/user.py:88
+msgid "Please enter a login name."
+msgstr ""
+
+#: ckan/controllers/user.py:91
+#, python-format
+msgid ""
+"That login name is not valid. It must be at least 3 characters, restricted to"
+" alphanumerics and these symbols: %s"
+msgstr ""
+
+#: ckan/controllers/user.py:94
+msgid "That login name is not available."
+msgstr ""
+
+#: ckan/controllers/user.py:97
+msgid "Please enter a password."
+msgstr ""
+
+#: ckan/controllers/user.py:122
 #, python-format
 msgid "Welcome back, %s"
 msgstr ""
 
-#: ckan/controllers/user.py:143
-msgid "Changed user details"
+#: ckan/controllers/user.py:160
+msgid ""
+"Edit not allowed as it looks like spam. Please avoid links in your "
+"description."
 msgstr ""
 
-#: ckan/controllers/user.py:159
+#: ckan/controllers/user.py:181
 msgid "Your account has been updated."
 msgstr ""
 
-#: ckan/controllers/user.py:174
+#: ckan/controllers/user.py:201
 msgid "Your password must be 4 characters or longer."
 msgstr ""
 
-#: ckan/controllers/user.py:176
+#: ckan/controllers/user.py:203
 msgid "The passwords you entered do not match."
 msgstr ""
 
-#: ckan/forms/common.py:22 ckan/logic/validators.py:59
+#: ckan/forms/common.py:25 ckan/logic/validators.py:69
 #, python-format
 msgid "Name must be at least %s characters long"
 msgstr ""
 
-#: ckan/forms/common.py:24 ckan/logic/validators.py:61
+#: ckan/forms/common.py:27 ckan/logic/validators.py:71
 msgid ""
 "Name must be purely lowercase alphanumeric (ascii) characters and these "
 "symbols: -_"
 msgstr ""
 
-#: ckan/forms/common.py:37 ckan/logic/validators.py:79
+#: ckan/forms/common.py:40 ckan/logic/validators.py:89
 msgid "Package name already exists in database"
 msgstr ""
 
-#: ckan/forms/common.py:45 ckan/logic/validators.py:95
+#: ckan/forms/common.py:48 ckan/logic/validators.py:119
 msgid "Group name already exists in database"
 msgstr ""
 
-#: ckan/forms/common.py:134
+#: ckan/forms/common.py:137
 #, python-format
 msgid "Value does not match required format: %s"
 msgstr ""
 
-#: ckan/forms/common.py:151 ckan/forms/common.py:767
+#: ckan/forms/common.py:154 ckan/forms/common.py:770
+#: ckan/templates/package/new_package_form.html:111
 msgid "(None)"
 msgstr ""
 
-#: ckan/forms/common.py:342
+#: ckan/forms/common.py:345 ckan/templates/package/new_package_form.html:94
 msgid "Package resource(s) incomplete."
 msgstr ""
 
-#: ckan/forms/common.py:503 ckan/logic/validators.py:101
+#: ckan/forms/common.py:506 ckan/logic/validators.py:125
 #, python-format
 msgid "Tag \"%s\" length is less than minimum %s"
 msgstr ""
 
-#: ckan/forms/common.py:505 ckan/logic/validators.py:109
+#: ckan/forms/common.py:508 ckan/logic/validators.py:133
 #, python-format
 msgid "Tag \"%s\" must be alphanumeric characters or symbols: -_."
 msgstr ""
 
-#: ckan/forms/common.py:507 ckan/logic/validators.py:117
+#: ckan/forms/common.py:510 ckan/logic/validators.py:141
 #, python-format
 msgid "Tag \"%s\" must not be uppercase"
 msgstr ""
 
-#: ckan/forms/common.py:524
+#: ckan/forms/common.py:527 ckan/logic/validators.py:103
 #, python-format
 msgid "Duplicate key \"%s\""
 msgstr ""
 
-#: ckan/forms/common.py:527
+#: ckan/forms/common.py:530
 #, python-format
 msgid "Extra key-value pair: key is not set for value \"%s\"."
 msgstr ""
 
-#: ckan/forms/common.py:777
+#: ckan/forms/common.py:780 ckan/templates/package/new_package_form.html:117
 msgid "Cannot add any groups."
 msgstr ""
 
-#: ckan/forms/common.py:792
+#: ckan/forms/common.py:795 ckan/templates/package/new_package_form.html:108
 msgid "Group"
 msgstr ""
 
-#: ckan/forms/common.py:822
+#: ckan/forms/common.py:825
 #, python-format
 msgid ""
 "Can't derived new group selection from serialized value structured like this:"
 " %s"
 msgstr ""
 
-#: ckan/forms/common.py:902
+#: ckan/forms/common.py:905
 msgid "other - please specify"
 msgstr ""
 
@@ -355,81 +352,86 @@
 msgid "Name"
 msgstr ""
 
-#: ckan/forms/group.py:63
+#: ckan/forms/group.py:63 ckan/templates/group/new_group_form.html:16
 msgid "Details"
 msgstr ""
 
 #: ckan/forms/group.py:64 ckan/forms/package.py:102 ckan/forms/package.py:112
+#: ckan/logic/action/update.py:29 ckan/logic/action/update.py:31
+#: ckan/logic/action/update.py:41 ckan/logic/action/update.py:43
+#: ckan/templates/group/new_group_form.html:41
+#: ckan/templates/package/new_package_form.html:154
 msgid "Extras"
 msgstr ""
 
-#: ckan/forms/group.py:87
+#: ckan/forms/group.py:87 ckan/templates/group/new_group_form.html:83
 msgid "Package"
 msgstr ""
 
-#: ckan/forms/group.py:88
+#: ckan/forms/group.py:88 ckan/templates/group/new_group_form.html:79
 msgid "Add packages"
 msgstr ""
 
-#: ckan/forms/package.py:34
+#: ckan/forms/package.py:34 ckan/templates/package/new_package_form.html:21
 msgid "A short descriptive title for the data set."
 msgstr ""
 
-#: ckan/forms/package.py:35
+#: ckan/forms/package.py:35 ckan/templates/package/new_package_form.html:22
 msgid ""
 "It should not be a description though - save that for the Notes field. Do not"
 " give a trailing full stop."
 msgstr ""
 
-#: ckan/forms/package.py:39
+#: ckan/forms/package.py:39 ckan/templates/package/new_package_form.html:27
 msgid "A unique identifier for the package."
 msgstr ""
 
-#: ckan/forms/package.py:40
+#: ckan/forms/package.py:40 ckan/templates/package/new_package_form.html:28
 msgid ""
 "It should be broadly humanly readable, in the spirit of Semantic Web URIs. "
 "Only use an acronym if it is widely recognised. Renaming is possible but "
 "discouraged."
 msgstr ""
 
-#: ckan/forms/package.py:41
+#: ckan/forms/package.py:41 ckan/templates/package/new_package_form.html:29
 msgid "2+ characters, lowercase, using only 'a-z0-9' and '-_'"
 msgstr ""
 
-#: ckan/forms/package.py:45
+#: ckan/forms/package.py:45 ckan/templates/package/new_package_form.html:139
 msgid "A number representing the version (if applicable)"
 msgstr ""
 
-#: ckan/forms/package.py:50
+#: ckan/forms/package.py:50 ckan/templates/package/new_package_form.html:34
 msgid "The URL for the web page describing the data (not the data itself)."
 msgstr ""
 
-#: ckan/forms/package.py:51
+#: ckan/forms/package.py:51 ckan/templates/package/new_package_form.html:35
 msgid "e.g. http://www.example.com/growth-figures.html"
 msgstr ""
 
-#: ckan/forms/package.py:55
+#: ckan/forms/package.py:55 ckan/templates/package/new_package_form.html:125
 msgid ""
 "The name of the main contact, for enquiries about this particular dataset, "
 "using the e-mail address in the following field."
 msgstr ""
 
-#: ckan/forms/package.py:59
+#: ckan/forms/package.py:59 ckan/templates/package/new_package_form.html:132
 msgid ""
 "If there is another important contact person (in addition to the person in "
 "the Author field) then provide details here."
 msgstr ""
 
-#: ckan/forms/package.py:63
+#: ckan/forms/package.py:63 ckan/templates/package/new_package_form.html:44
 msgid "Licence"
 msgstr ""
 
-#: ckan/forms/package.py:64
+#: ckan/forms/package.py:64 ckan/templates/package/new_package_form.html:52
 msgid "The licence under which the dataset is released."
 msgstr ""
 
 #: ckan/forms/package.py:68 ckan/forms/package.py:112
-#: ckan/templates/layout_base.html:101 ckan/templates/layout_base.html:180
+#: ckan/templates/layout_base.html:104 ckan/templates/layout_base.html:189
+#: ckan/templates/package/new_package_form.html:54
 #: ckan/templates/package/read.html:18 ckan/templates/tag/index.html:6
 #: ckan/templates/tag/index.html:9
 msgid "Tags"
@@ -442,11 +444,11 @@
 "conventions, see <a href=\"%s\">this wiki page</a>."
 msgstr ""
 
-#: ckan/forms/package.py:70
+#: ckan/forms/package.py:70 ckan/templates/package/new_package_form.html:61
 msgid "e.g. pollution rivers water-quality"
 msgstr ""
 
-#: ckan/forms/package.py:74
+#: ckan/forms/package.py:74 ckan/templates/package/new_package_form.html:91
 msgid "The files containing the data or address of the APIs for accessing it."
 msgstr ""
 
@@ -465,21 +467,22 @@
 "information you want to add to describe the resource.<br />"
 msgstr ""
 
-#: ckan/forms/package.py:76
+#: ckan/forms/package.py:76 ckan/templates/package/new_package_form.html:93
 msgid ""
 "Format choices: CSV | RDF | XML | XBRL | SDMX | HTML+RDFa | Other as "
 "appropriate"
 msgstr ""
 
 #: ckan/forms/package.py:80 ckan/forms/package.py:111
+#: ckan/templates/package/new_package_form.html:38
 msgid "Notes"
 msgstr ""
 
-#: ckan/forms/package.py:81
+#: ckan/forms/package.py:81 ckan/templates/package/new_package_form.html:40
 msgid "The main description of the dataset"
 msgstr ""
 
-#: ckan/forms/package.py:82
+#: ckan/forms/package.py:82 ckan/templates/package/new_package_form.html:41
 msgid ""
 "It is often displayed with the package title. In particular, it should start "
 "with a short sentence that describes the data set succinctly, because the "
@@ -491,52 +494,57 @@
 msgid "You can use %sMarkdown formatting%s here."
 msgstr ""
 
-#: ckan/forms/package.py:94
+#: ckan/forms/package.py:94 ckan/templates/package/new_package_form.html:17
 msgid "Basic information"
 msgstr ""
 
 #: ckan/forms/package.py:96 ckan/forms/package.py:111
+#: ckan/logic/action/update.py:27 ckan/templates/package/new_package_form.html:67
 #: ckan/templates/package/read_core.html:60
 msgid "Resources"
 msgstr ""
 
-#: ckan/forms/package.py:97 ckan/templates/layout_base.html:102
-#: ckan/templates/layout_base.html:181 ckan/templates/group/index.html:9
+#: ckan/forms/package.py:97 ckan/templates/layout_base.html:105
+#: ckan/templates/layout_base.html:190 ckan/templates/group/index.html:9
+#: ckan/templates/package/new_package_form.html:98
 #: ckan/templates/package/read.html:26 ckan/templates/revision/read.html:67
 msgid "Groups"
 msgstr ""
 
 #: ckan/forms/package.py:98 ckan/forms/package.py:105
+#: ckan/templates/package/new_package_form.html:121
 msgid "Detail"
 msgstr ""
 
 #: ckan/forms/package.py:110 ckan/templates/_util.html:97
-#: ckan/templates/_util.html:110
+#: ckan/templates/_util.html:110 ckan/templates/group/new_group_form.html:23
+#: ckan/templates/package/new_package_form.html:19
 msgid "Title"
 msgstr ""
 
-#: ckan/forms/package.py:110
+#: ckan/forms/package.py:110 ckan/templates/package/new_package_form.html:137
 msgid "Version"
 msgstr ""
 
-#: ckan/forms/package.py:110
+#: ckan/forms/package.py:110 ckan/templates/package/new_package_form.html:32
 msgid "URL"
 msgstr ""
 
 #: ckan/forms/package.py:111 ckan/templates/_util.html:282
 #: ckan/templates/group/history.html:35 ckan/templates/package/history.html:41
+#: ckan/templates/package/new_package_form.html:123
 msgid "Author"
 msgstr ""
 
-#: ckan/forms/package.py:111
+#: ckan/forms/package.py:111 ckan/templates/package/new_package_form.html:127
 msgid "Author email"
 msgstr ""
 
-#: ckan/forms/package.py:111
+#: ckan/forms/package.py:111 ckan/templates/package/new_package_form.html:130
 msgid "Maintainer"
 msgstr ""
 
-#: ckan/forms/package.py:112
+#: ckan/forms/package.py:112 ckan/templates/package/new_package_form.html:134
 msgid "Maintainer email"
 msgstr ""
 
@@ -545,7 +553,8 @@
 msgid "License"
 msgstr ""
 
-#: ckan/forms/package.py:112
+#: ckan/forms/package.py:112 ckan/templates/group/new_group_form.html:30
+#: ckan/templates/package/new_package_form.html:142
 msgid "State"
 msgstr ""
 
@@ -572,52 +581,53 @@
 msgid "Cannot render package description"
 msgstr ""
 
-#: ckan/lib/package_saver.py:43
+#: ckan/lib/package_saver.py:44
 msgid "No web page given"
 msgstr ""
 
-#: ckan/lib/package_saver.py:140
+#: ckan/lib/package_saver.py:141 ckan/logic/validators.py:20
 msgid "No links are allowed in the log_message."
 msgstr ""
 
-#: ckan/logic/validators.py:9
+#: ckan/logic/validators.py:10
 #, python-format
 msgid "Cannot change value of key from %s to %s. This key is read-only"
 msgstr ""
 
-#: ckan/logic/validators.py:20 ckan/logic/validators.py:46
-#: ckan/logic/action/update.py:30
+#: ckan/logic/validators.py:30 ckan/logic/validators.py:56
+#: ckan/logic/action/update.py:86
 msgid "Package was not found."
 msgstr ""
 
-#: ckan/logic/validators.py:31 ckan/logic/action/create.py:167
+#: ckan/logic/validators.py:41 ckan/logic/action/create.py:182
 #, python-format
 msgid "Package with name %r does not exist."
 msgstr ""
 
-#: ckan/logic/action/create.py:44 ckan/logic/action/create.py:129
+#: ckan/logic/action/create.py:56 ckan/logic/action/create.py:143
+#: ckan/logic/action/update.py:103 ckan/logic/action/update.py:189
 #, python-format
 msgid "REST API: Create object %s"
 msgstr ""
 
-#: ckan/logic/action/create.py:105
+#: ckan/logic/action/create.py:118
 #, python-format
 msgid "REST API: Create package relationship: %s %s %s"
 msgstr ""
 
-#: ckan/logic/action/create.py:154
+#: ckan/logic/action/create.py:169
 msgid "You must supply a package id or name (parameter \"package\")."
 msgstr ""
 
-#: ckan/logic/action/create.py:156
+#: ckan/logic/action/create.py:171
 msgid "You must supply a rating (parameter \"rating\")."
 msgstr ""
 
-#: ckan/logic/action/create.py:161
+#: ckan/logic/action/create.py:176
 msgid "Rating must be an integer value."
 msgstr ""
 
-#: ckan/logic/action/create.py:165
+#: ckan/logic/action/create.py:180
 #, python-format
 msgid "Rating must be between %i and %i."
 msgstr ""
@@ -632,12 +642,19 @@
 msgid "REST API: Delete %s"
 msgstr ""
 
-#: ckan/logic/action/update.py:42 ckan/logic/action/update.py:128
-#, python-format
-msgid "REST API: Update object %s"
+#: ckan/logic/action/update.py:27
+msgid "Package resource(s) incomplete"
 msgstr ""
 
-#: ckan/logic/action/update.py:60
+#: ckan/logic/action/update.py:29 ckan/logic/action/update.py:41
+msgid "Missing Value"
+msgstr ""
+
+#: ckan/logic/action/update.py:64
+msgid "Group was not found."
+msgstr ""
+
+#: ckan/logic/action/update.py:123
 #, python-format
 msgid "REST API: Update package relationship: %s %s %s"
 msgstr ""
@@ -707,7 +724,10 @@
 msgid "Number of packages"
 msgstr ""
 
-#: ckan/templates/_util.html:97 ckan/templates/package/read_core.html:36
+#: ckan/templates/_util.html:97 ckan/templates/group/new_group_form.html:27
+#: ckan/templates/package/new_package_form.html:73
+#: ckan/templates/package/new_package_form.html:92
+#: ckan/templates/package/read_core.html:36
 msgid "Description"
 msgstr ""
 
@@ -764,7 +784,9 @@
 msgid "Log Message"
 msgstr ""
 
-#: ckan/templates/_util.html:306 ckan/templates/package/form_extra_fields.html:22
+#: ckan/templates/_util.html:306 ckan/templates/group/new_group_form.html:49
+#: ckan/templates/package/form_extra_fields.html:22
+#: ckan/templates/package/new_package_form.html:162
 #: ckan/templates/revision/read.html:20
 msgid "Delete"
 msgstr ""
@@ -802,109 +824,114 @@
 msgid "Home"
 msgstr ""
 
-#: ckan/templates/layout_base.html:99 ckan/templates/layout_base.html:166
-#: ckan/templates/package/search.html:31 ckan/templates/package/search_form.html:17
+#: ckan/templates/layout_base.html:99 ckan/templates/layout_base.html:169
+#: ckan/templates/package/search.html:47 ckan/templates/package/search_form.html:17
 msgid "Search"
 msgstr ""
 
-#: ckan/templates/layout_base.html:100 ckan/templates/package/search.html:14
+#: ckan/templates/layout_base.html:103 ckan/templates/package/search.html:14
 msgid "Add a package"
 msgstr ""
 
-#: ckan/templates/layout_base.html:103 ckan/templates/layout_base.html:191
+#: ckan/templates/layout_base.html:106 ckan/templates/layout_base.html:200
 #: ckan/templates/home/about.html:6
 msgid "About"
 msgstr ""
 
-#: ckan/templates/layout_base.html:132
+#: ckan/templates/layout_base.html:135
 msgid "Master content template placeholder … please replace me."
 msgstr ""
 
-#: ckan/templates/layout_base.html:163 ckan/templates/group/edit_form.html:10
-#: ckan/templates/revision/read.html:48
+#: ckan/templates/layout_base.html:166 ckan/templates/group/edit_form.html:10
+#: ckan/templates/group/new_group_form.html:66 ckan/templates/revision/read.html:48
 msgid "Packages"
 msgstr ""
 
-#: ckan/templates/layout_base.html:167
+#: ckan/templates/layout_base.html:170
 msgid "Register a new Package"
 msgstr ""
 
-#: ckan/templates/layout_base.html:168 ckan/templates/revision/list.html:5
+#: ckan/templates/layout_base.html:171 ckan/templates/revision/list.html:5
 #: ckan/templates/revision/list.html:8
 msgid "Revision History"
 msgstr ""
 
-#: ckan/templates/layout_base.html:169
+#: ckan/templates/layout_base.html:172 ckan/templates/package/search.html:37
 msgid "API"
 msgstr ""
 
-#: ckan/templates/layout_base.html:170
+#: ckan/templates/layout_base.html:173 ckan/templates/package/search.html:38
 msgid "API Docs"
 msgstr ""
 
-#: ckan/templates/layout_base.html:177
+#: ckan/templates/layout_base.html:179
+#, python-format
+msgid "Full %s dump"
+msgstr ""
+
+#: ckan/templates/layout_base.html:186
 msgid "Groups & Tags"
 msgstr ""
 
-#: ckan/templates/layout_base.html:182
+#: ckan/templates/layout_base.html:191
 msgid "Create a new Group"
 msgstr ""
 
-#: ckan/templates/layout_base.html:183
+#: ckan/templates/layout_base.html:192
 #: ckan/templates/authorization_group/index.html:6
 #: ckan/templates/authorization_group/index.html:9
 #: ckan/templates/authorization_group/layout.html:23
 msgid "Authorization Groups"
 msgstr ""
 
-#: ckan/templates/layout_base.html:184
+#: ckan/templates/layout_base.html:193
 msgid "Create a new Authorization Group"
 msgstr ""
 
-#: ckan/templates/layout_base.html:195
+#: ckan/templates/layout_base.html:204
 msgid "Project Home Page"
 msgstr ""
 
-#: ckan/templates/layout_base.html:198
+#: ckan/templates/layout_base.html:207
 msgid "Contact Us"
 msgstr ""
 
-#: ckan/templates/layout_base.html:201 ckan/templates/user/login.html:26
+#: ckan/templates/layout_base.html:210 ckan/templates/user/login.html:26
 msgid "Privacy Policy"
 msgstr ""
 
-#: ckan/templates/layout_base.html:209
+#: ckan/templates/layout_base.html:218
 msgid "Language"
 msgstr ""
 
-#: ckan/templates/layout_base.html:234
+#: ckan/templates/layout_base.html:243
 msgid "Credits"
 msgstr ""
 
-#: ckan/templates/layout_base.html:237
+#: ckan/templates/layout_base.html:246
 msgid ""
 "[1:]\n"
 "                An\n"
 "                [2:Open Knowledge Foundation] Project"
 msgstr ""
 
-#: ckan/templates/layout_base.html:243
+#: ckan/templates/layout_base.html:252
 msgid "Open Knowledge Foundation"
 msgstr ""
 
-#: ckan/templates/layout_base.html:247 ckan/templates/layout_base.html:252
+#: ckan/templates/layout_base.html:256 ckan/templates/layout_base.html:261
 msgid "This Content and Data is Open"
 msgstr ""
 
-#: ckan/templates/layout_base.html:269
+#: ckan/templates/layout_base.html:278
 msgid "CKAN"
 msgstr ""
 
-#: ckan/templates/layout_base.html:270
+#: ckan/templates/layout_base.html:279
 msgid "Powered by CKAN"
 msgstr ""
 
-#: ckan/templates/layout_base.html:272
+#: ckan/templates/layout_base.html:281
 #, python-format
 msgid "v%(version)s"
 msgstr ""
@@ -917,21 +944,41 @@
 msgid "Authorization for authorization group:"
 msgstr ""
 
-#: ckan/templates/authorization_group/authz.html:15
-#: ckan/templates/group/authz.html:15 ckan/templates/package/authz.html:15
+#: ckan/templates/authorization_group/authz.html:13
+#: ckan/templates/group/authz.html:12 ckan/templates/package/authz.html:12
 msgid "Update Existing Roles"
 msgstr ""
 
-#: ckan/templates/authorization_group/authz.html:20
-msgid "Create New User or Authorization Group Roles"
+#: ckan/templates/authorization_group/authz.html:17
+#: ckan/templates/authorization_group/authz.html:35
+#: ckan/templates/authorization_group/edit_form.html:25
+#: ckan/templates/group/authz.html:16 ckan/templates/group/authz.html:34
+#: ckan/templates/group/edit_form.html:23 ckan/templates/package/authz.html:16
+#: ckan/templates/package/authz.html:34 ckan/templates/package/edit_form.html:29
+#: ckan/templates/user/edit.html:45
+msgid "Save"
 msgstr ""
 
-#: ckan/templates/authorization_group/authz.html:25
-#: ckan/templates/authorization_group/edit_form.html:25
-#: ckan/templates/group/authz.html:25 ckan/templates/group/edit_form.html:23
-#: ckan/templates/package/authz.html:25 ckan/templates/package/edit_form.html:29
-#: ckan/templates/user/edit.html:45
-msgid "Save"
+#: ckan/templates/authorization_group/authz.html:22
+#: ckan/templates/group/authz.html:21 ckan/templates/package/authz.html:21
+msgid "Add Roles for Any User"
+msgstr ""
+
+#: ckan/templates/authorization_group/authz.html:26
+#: ckan/templates/authorization_group/authz.html:44
+#: ckan/templates/group/authz.html:25 ckan/templates/group/authz.html:43
+#: ckan/templates/package/authz.html:25 ckan/templates/package/authz.html:43
+msgid "Add"
+msgstr ""
+
+#: ckan/templates/authorization_group/authz.html:31
+#: ckan/templates/group/authz.html:30 ckan/templates/package/authz.html:30
+msgid "Existing Roles for Authorization Groups"
+msgstr ""
+
+#: ckan/templates/authorization_group/authz.html:40
+#: ckan/templates/group/authz.html:39 ckan/templates/package/authz.html:39
+msgid "Add Roles for Any Authorization Group"
 msgstr ""
 
 #: ckan/templates/authorization_group/edit.html:5
@@ -967,7 +1014,7 @@
 msgstr ""
 
 #: ckan/templates/authorization_group/layout.html:16
-#: ckan/templates/group/layout.html:32 ckan/templates/package/layout.html:20
+#: ckan/templates/group/layout.html:32 ckan/templates/package/layout.html:17
 msgid "Authorization"
 msgstr ""
 
@@ -1027,10 +1074,6 @@
 msgid "Authorization for group:"
 msgstr ""
 
-#: ckan/templates/group/authz.html:20 ckan/templates/package/authz.html:20
-msgid "Create New User Roles"
-msgstr ""
-
 #: ckan/templates/group/edit.html:5
 msgid "- Edit - Groups"
 msgstr ""
@@ -1040,6 +1083,7 @@
 msgstr ""
 
 #: ckan/templates/group/edit_form.html:17
+#: ckan/templates/group/new_group_form.html:75
 msgid "There are no packages currently in this group."
 msgstr ""
 
@@ -1057,7 +1101,7 @@
 msgstr ""
 
 #: ckan/templates/group/history.html:27 ckan/templates/package/history.html:33
-#: ckan/templates/package/new.html:37
+#: ckan/templates/package/new.html:39
 msgid "Error:"
 msgstr ""
 
@@ -1098,7 +1142,7 @@
 msgid "Create a new group"
 msgstr ""
 
-#: ckan/templates/group/layout.html:30 ckan/templates/package/layout.html:18
+#: ckan/templates/group/layout.html:30 ckan/templates/package/layout.html:15
 msgid "History"
 msgstr ""
 
@@ -1110,6 +1154,55 @@
 msgid "New Group"
 msgstr ""
 
+#: ckan/templates/group/new_group_form.html:8 ckan/templates/package/form.html:7
+#: ckan/templates/package/new_package_form.html:9
+msgid "Errors in form"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:9 ckan/templates/package/form.html:8
+#: ckan/templates/package/new_package_form.html:10
+msgid "The form contains invalid entries:"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:18
+#: ckan/templates/package/new_package_form.html:25
+msgid "Name *"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:20
+msgid "Unique identifier"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:20
+msgid "for group."
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:20
+msgid "2+ chars, lowercase, using only 'a-z0-9' and '-_'"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:33
+#: ckan/templates/package/new_package_form.html:145
+msgid "active"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:34
+#: ckan/templates/package/new_package_form.html:146
+msgid "deleted"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:54
+#: ckan/templates/package/form_extra_fields.html:12
+#: ckan/templates/package/new_package_form.html:167
+msgid "New key"
+msgstr ""
+
+#: ckan/templates/group/new_group_form.html:56
+#: ckan/templates/package/form_extra_fields.html:26
+#: ckan/templates/package/new_package_form.html:169
+msgid "with value"
+msgstr ""
+
 #: ckan/templates/group/read.html:6
 msgid "- Groups"
 msgstr ""
@@ -1267,33 +1360,41 @@
 msgid "Edit Data Package:"
 msgstr ""
 
-#: ckan/templates/package/edit.html:29 ckan/templates/package/new.html:31
+#: ckan/templates/package/edit.html:29 ckan/templates/package/new.html:33
 #: ckan/templates/user/edit.html:52
 msgid "Preview"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:13
+#: ckan/templates/package/new_package_form.html:179
 msgid "Edit summary (briefly describe the changes you have made)"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:17
-#: ckan/templates/package/edit_form.html:20 ckan/templates/revision/read.html:39
+#: ckan/templates/package/edit_form.html:20
+#: ckan/templates/package/new_package_form.html:183
+#: ckan/templates/package/new_package_form.html:186
+#: ckan/templates/revision/read.html:39
 msgid "Author:"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:21
+#: ckan/templates/package/new_package_form.html:187
 msgid "Since you have not signed in this will just be your IP address."
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:23
+#: ckan/templates/package/new_package_form.html:189
 msgid "Click here to sign in"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:23
+#: ckan/templates/package/new_package_form.html:189
 msgid "before saving (opens in new window)."
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:33
+#: ckan/templates/package/new_package_form.html:199
 msgid "Important:"
 msgstr ""
 
@@ -1304,45 +1405,35 @@
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:34
+#: ckan/templates/package/new_package_form.html:200
 msgid "license page"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:35
+#: ckan/templates/package/new_package_form.html:200
 msgid ". Please"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:35
+#: ckan/templates/package/new_package_form.html:200
 msgid "refrain"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:35
+#: ckan/templates/package/new_package_form.html:200
 msgid "from editing this page if you are"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:35
+#: ckan/templates/package/new_package_form.html:200
 msgid "not"
 msgstr ""
 
 #: ckan/templates/package/edit_form.html:35
+#: ckan/templates/package/new_package_form.html:201
 msgid "happy to do this."
 msgstr ""
 
-#: ckan/templates/package/form.html:7
-msgid "Errors in form"
-msgstr ""
-
-#: ckan/templates/package/form.html:8
-msgid "The form contains invalid entries:"
-msgstr ""
-
-#: ckan/templates/package/form_extra_fields.html:12
-msgid "New key"
-msgstr ""
-
-#: ckan/templates/package/form_extra_fields.html:26
-msgid "with value"
-msgstr ""
-
 #: ckan/templates/package/history.html:10
 msgid "Updates"
 msgstr ""
@@ -1355,18 +1446,217 @@
 msgid "Package History"
 msgstr ""
 
-#: ckan/templates/package/layout.html:16
-msgid "Comments & Questions"
-msgstr ""
-
 #: ckan/templates/package/new.html:6
 msgid "New - Data Packages"
 msgstr ""
 
-#: ckan/templates/package/new.html:27
+#: ckan/templates/package/new.html:29
 msgid "Register a New Data Package"
 msgstr ""
 
+#: ckan/templates/package/new_package_form.html:42
+msgid "You can use"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:42
+msgid "Markdown formatting"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:42
+msgid "here."
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:60
+msgid ""
+"Terms that may link this dataset to similar ones. For more information on "
+"conventions, see"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:60
+msgid "this wiki page"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:71
+msgid "URL*"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:72
+#: ckan/templates/package/read_core.html:37
+msgid "Format"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:74
+#: ckan/templates/package/read_core.html:38
+msgid "Hash"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:92
+msgid ""
+"These can be repeated as required. For example if the data is being supplied "
+"in multiple formats, or split into different areas or time periods, each file"
+" is a different 'resource' which should be described differently. They will "
+"all appear on the dataset page on CKAN together."
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:92
+msgid "URL:"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:92
+msgid ""
+"This is the Internet link directly to the data - by selecting this link in a "
+"web browser, the user will immediately download the full data set. Note that "
+"datasets are not hosted on this site, but by the publisher of the data. "
+"Alternatively the URL can point to an API server such as a SPARQL endpoint or"
+" JSON-P service."
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:92
+msgid "Format:"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:92
+msgid "This should give the file format in which the data is supplied."
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:92
+msgid "Any information you want to add to describe the resource."
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:140
+msgid "e.g. 1.2.0"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.html:199
+msgid ""
+"By submitting content, you agree to release your contributions\n"
+"  under the open license specified on the"
+msgstr ""
+
+#: ckan/templates/package/new_package_form.js:1
+msgid ""
+"<script type=\"text/javascript\">\n"
+"//<![CDATA[\n"
+"(function($){\n"
+"    $.fn.ajaxCreateSlug = function(name, url) {\n"
+"        var title = this;\n"
+"        var updater = {\n"
+"            init: function(title, name) {\n"
+"                // Add a new element where the validity of the package name "
+"can be displayed\n"
+"                this.name_field = name;\n"
+"                this.title_field = title;\n"
+"                this.name_field.parent().append('<div "
+"id=\"package_name_valid_msg\"></div>');\n"
+"                this.title_field.blur(this.title_change_handler())\n"
+"                this.title_field.keyup(this.title_change_handler())\n"
+"                this.name_field.keyup(this.name_change_handler());\n"
+"                this.name_field.blur(this.name_blur_handler());\n"
+"                this.url = url;\n"
+"            },\n"
+"            title_change_handler: function() {\n"
+"                var self = this;\n"
+"                return function() {\n"
+"                    if (!self.name_changed && "
+"self.title_field.val().replace(/^\\s+|\\s+$/g, '')) {\n"
+"                        self.update(self.title_field.val(), function(data) "
+"{self.name_field.val(data.name)});\n"
+"                    }\n"
+"                }\n"
+"            },\n"
+"            name_blur_handler: function() {\n"
+"                var self = this;\n"
+"                return function() {\n"
+"                    // Reset if the name is emptied\n"
+"                    if (!self.name_field.val().replace(/^\\s+|\\s+$/g, '')){\n"
+"                        self.name_changed = false;\n"
+"                        $('#package_name_valid_msg').html('');\n"
+"                    } else {\n"
+"                        self.update(self.name_field.val(), function(data) {\n"
+"                            self.name_field.val(data.name)\n"
+"                        });\n"
+"                    }\n"
+"                };\n"
+"            },\n"
+"            name_change_handler: function() {\n"
+"                var self = this;\n"
+"                return function() {\n"
+"                    // Reset if the name is emptied\n"
+"                    if (!self.name_field.val().replace(/^\\s+|\\s+$/g, '')){\n"
+"                        self.name_changed = false;\n"
+"                        $('#package_name_valid_msg').html('');\n"
+"                    } else {\n"
+"                        self.name_changed = true;\n"
+"                        self.update(self.name_field.val(), function(data) {\n"
+"                            if (self.name_field.val().length >= data.name) {\n"
+"                                self.name_field.val(data.name);\n"
+"                            }\n"
+"                        });\n"
+"                    }\n"
+"                };\n"
+"            },\n"
+"            // Keep a variable where we can store whether the name field has "
+"been\n"
+"            // directly modified by the user or not. If it has, we should no "
+"longer\n"
+"            // fetch updates.\n"
+"            name_changed: false,\n"
+"            // Create a function for fetching the value and updating the "
+"result\n"
+"            perform_update: function(value, on_success){\n"
+"                var self = this;\n"
+"                $.ajax({\n"
+"                    url: self.url,\n"
+"                    data: 'title=' + value,\n"
+"                    dataType: 'jsonp',\n"
+"                    type: 'get',\n"
+"                    jsonpCallback: 'callback',\n"
+"                    success: function (data) {\n"
+"                        if (on_success) {\n"
+"                            on_success(data);\n"
+"                        }\n"
+"                        var valid_msg = $('#package_name_valid_msg');\n"
+"                        if (data.valid) {\n"
+"                            valid_msg.html('<span style=\"font-weight: bold; "
+"color: #0c0\">This package name is available!</span>');\n"
+"                        } else {\n"
+"                            valid_msg.html('<span style=\"font-weight: bold; "
+"color: #c00\">This package name is already used, please use a different "
+"name</span>');\n"
+"                        }\n"
+"                    }\n"
+"                });\n"
+"            },\n"
+"            // We only want to perform the update if there hasn't been a "
+"change for say 200ms\n"
+"            timer: null,\n"
+"            update: function(value, on_success) {\n"
+"                var self = this;\n"
+"                if (this.timer) {\n"
+"                    clearTimeout(this.timer)\n"
+"                };\n"
+"                this.timer = setTimeout(function () {\n"
+"                    self.perform_update(value, on_success)\n"
+"                }, 200);\n"
+"            }\n"
+"        }\n"
+"        updater.init(title, $(name), url);\n"
+"        return title;\n"
+"    };\n"
+"})( jQuery );\n"
+"$(document).ready(function() {\n"
+"    $('#title').ajaxCreateSlug('#name', '/api/2/util/package/create_slug');\n"
+"});\n"
+"\n"
+"$(document).ready(function () {\n"
+"    if (!$('#preview').length) {\n"
+"        $(\"#title\").focus();\n"
+"    }\n"
+"});\n"
+"//]]>\n"
+"</script>"
+msgstr ""
+
 #: ckan/templates/package/read.html:6
 msgid "- Data Packages"
 msgstr ""
@@ -1437,14 +1727,6 @@
 msgid "Downloads & Resources"
 msgstr ""
 
-#: ckan/templates/package/read_core.html:37
-msgid "Format"
-msgstr ""
-
-#: ckan/templates/package/read_core.html:38
-msgid "Hash"
-msgstr ""
-
 #: ckan/templates/package/read_core.html:48
 msgid "Download"
 msgstr ""
@@ -1549,18 +1831,42 @@
 msgid "Register it now"
 msgstr ""
 
+#: ckan/templates/package/search.html:29
+msgid "Other access"
+msgstr ""
+
+#: ckan/templates/package/search.html:35
+msgid "You can also access this registry using the"
+msgstr ""
+
 #: ckan/templates/package/search.html:37
+msgid "(see"
+msgstr ""
+
+#: ckan/templates/package/search.html:38
+msgid "or download a"
+msgstr ""
+
+#: ckan/templates/package/search.html:39
+msgid "full"
+msgstr ""
+
+#: ckan/templates/package/search.html:39
+msgid "dump"
+msgstr ""
+
+#: ckan/templates/package/search.html:53
 msgid ""
 "[1:There was an error while searching.] \n"
 "            Please try another search term."
 msgstr ""
 
-#: ckan/templates/package/search.html:41
+#: ckan/templates/package/search.html:57
 #, python-format
 msgid "[1:%(item_count)s] packages found"
 msgstr ""
 
-#: ckan/templates/package/search.html:44
+#: ckan/templates/package/search.html:60
 msgid "Would you like to [1:create a new package?]"
 msgstr ""
 


--- a/ckan/lib/ckan-to-talis.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-import sys
-from optparse import OptionParser
-
-import ckanclient
-import talis
-
-class TalisLoader:
-    def __init__(self, ckan_host, store, talisuser, talispassword):
-        api_key = None
-        
-        # ckan connection
-        if not ckan_host.startswith('http://'):
-            ckan_host = 'http://' + ckan_host
-        ckan_host = ckan_host + '/api'
-        self.ckan = ckanclient.CkanClient(base_location=ckan_host,
-                                          api_key=api_key)
-
-        # talis connection
-        talis.TalisLogin.init(store, talisuser, talispassword)
-        self._talis = talis.Talis()
-        
-    def get_package_names(self):
-        res = self.ckan.package_register_get()
-        assert self.ckan.last_status == 200, 'Error accessing \'%s\': %s' % (self.ckan.last_location, self.ckan.last_status)
-        packages = res
-        return packages
-
-    def load_to_talis(self, pkg_name):
-        print '=== Loading %s ===' % pkg_name
-        res = self.ckan.package_entity_get(pkg_name)
-        if self.ckan.last_status != 200:
-            print 'Failed to read CKAN package %s: %s' % (pkg_name, self.ckan.last_status)
-            return
-        pkg_dict = res
-        res = self._talis.post_pkg(pkg_dict)
-        if res:
-            print 'Failed to post package %s to Talis: %s' % (pkg_name, res)
-            return
-##        res = self._talis.get_pkg(pkg_name)
-##        assert res, res
-##        print 'Done!'
-        
-
-if __name__ == '__main__':
-    usage = 'usage: %prog [options] talis-password'
-    parser = OptionParser(usage=usage)
-#    parser.add_option('-k', '--apikey', dest='apikey', help='API key')
-    parser.add_option('-c', '--ckanhost', dest='ckanhost', help='CKAN host name', default='www.ckan.net')
-    parser.add_option('-s', '--store', dest='store', help='Talis store name', default='ckan-dev1')
-    parser.add_option('-u', '--talisusername', dest='talisuser', help='Talis user name', default='ckan')
-    parser.add_option('', '--startfrompackage', dest='startpackage', help='Package to start from', default='')
-    parser.add_option('-p', '--packages', dest='packages', help='Quoted list of packages to upload', default='')
-
-    (options, args) = parser.parse_args()
-    if len(args) != 1:
-        parser.print_help()
-        sys.exit(0)
-
-    talispassword = args[0]
-
-    te = TalisLoader(options.ckanhost, options.store, options.talisuser, talispassword)
-
-    pkg_names = te.get_package_names()
-    start_index = 0
-    if options.startpackage:
-        start_index = pkg_names.index(options.startpackage)
-        assert start_index
-        pkg_names = pkg_names[start_index:]
-    elif options.packages:
-        pkgs = options.packages.replace(',', ' ')
-        pkg_names = pkgs.split() if ' ' in pkgs else [pkgs]
-    for pkg_name in pkg_names:
-        te.load_to_talis(pkg_name)
-                              


--- a/ckan/lib/cli.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/lib/cli.py	Mon Jul 18 09:57:42 2011 +0100
@@ -298,6 +298,7 @@
     '''Gives sysadmin rights to a named user
 
     Usage:
+      sysadmin                      - lists sysadmins
       sysadmin list                 - lists sysadmins
       sysadmin add <user-name>      - add a user as a sysadmin
       sysadmin remove <user-name>   - removes user from sysadmins
@@ -306,14 +307,14 @@
     summary = __doc__.split('\n')[0]
     usage = __doc__
     max_args = 2
-    min_args = 1
+    min_args = 0
 
     def command(self):
         self._load_config()
         from ckan import model
 
-        cmd = self.args[0]
-        if cmd == 'list':
+        cmd = self.args[0] if self.args else None
+        if cmd == None or cmd == 'list':
             self.list()
         elif cmd == 'add':
             self.add()
@@ -325,9 +326,14 @@
     def list(self):
         from ckan import model
         print 'Sysadmins:'
-        sysadmins = model.Session.query(model.SystemRole).filter_by(role=model.Role.ADMIN).all()
+        sysadmins = model.Session.query(model.SystemRole).filter_by(role=model.Role.ADMIN)
+        print 'count = %i' % sysadmins.count()
         for sysadmin in sysadmins:
-            print 'name=%s id=%s' % (sysadmin.user.name, sysadmin.user.id)
+            user_or_authgroup = sysadmin.user or sysadmin.authorized_group
+            assert user_or_authgroup, 'Could not extract entity with this priviledge from: %r' % sysadmin
+            print '%s name=%s id=%s' % (user_or_authgroup.__class__.__name__,
+                                        user_or_authgroup.name,
+                                        user_or_authgroup.id)
 
     def add(self):
         from ckan import model
@@ -342,8 +348,10 @@
             print 'User "%s" not found' % username
             makeuser = raw_input('Create new user: %s? [y/n]' % username)
             if makeuser == 'y':
+                password = UserCmd.password_prompt()
                 print('Creating %s user' % username)
-                user = model.User(name=unicode(username))
+                user = model.User(name=unicode(username),
+                                  password=password)
             else:
                 print 'Exiting ...'
                 return
@@ -372,6 +380,7 @@
 
     Usage:
       user                            - lists users
+      user list                       - lists users
       user <user-name>                - shows user properties
       user add <user-name> [<apikey>] - add a user (prompts for password)
       user setpass <user-name>        - set user password (prompts)
@@ -399,6 +408,8 @@
                 self.search()
             elif cmd == 'setpass':
                 self.setpass()
+            elif cmd == 'list':
+                self.list()
             else:
                 self.show()
 
@@ -411,7 +422,8 @@
     def list(self):
         from ckan import model
         print 'Users:'
-        users = model.Session.query(model.User).all()
+        users = model.Session.query(model.User)
+        print 'count = %i' % users.count()        
         for user in users:
             print self.get_user_str(user)
 
@@ -424,7 +436,6 @@
 
     def setpass(self):
         from ckan import model
-        import getpass
         
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -433,14 +444,8 @@
         user = model.User.get(username)
         print('Editing user: %r' % user.name)
 
-        password1 = None
-        while not password1:
-            password1 = getpass.getpass('Password: ')
-        password2 = getpass.getpass('Confirm password: ')
-        if password1 != password2:
-            print 'Passwords do not match'
-            sys.exit(1)
-        user.password = password1
+        password = self.password_prompt()
+        user.password = password
         model.repo.commit_and_remove()
         print 'Done'
 
@@ -457,22 +462,9 @@
         for user in query.all():
             print self.get_user_str(user)
 
-    def add(self):
-        from ckan import model
+    @classmethod
+    def password_prompt(cls):
         import getpass
-        
-        if len(self.args) < 2:
-            print 'Need name of the user.'
-            return
-        username = self.args[1]
-        apikey = self.args[2] if len(self.args) > 2 else None
-
-        user = model.User.by_name(unicode(username))
-        if user:
-            print 'User "%s" already found' % username
-            sys.exit(1)
-        
-        print('Creating user: %r' % username)
         password1 = None
         while not password1:
             password1 = getpass.getpass('Password: ')
@@ -480,8 +472,27 @@
         if password1 != password2:
             print 'Passwords do not match'
             sys.exit(1)
+        return password1
+
+    def add(self):
+        from ckan import model
+        
+        if len(self.args) < 2:
+            print 'Need name of the user.'
+            return
+        username = self.args[1]
+        apikey = self.args[2] if len(self.args) > 2 else None
+        password = self.password_prompt()
+        user = model.User.by_name(unicode(username))
+        if user:
+            print 'User "%s" already found' % username
+            sys.exit(1)
+        
+        print('Creating user: %r' % username)
+
+        
         user_params = {'name': unicode(username),
-                   'password': password1}
+                       'password': password}
         if apikey:
             user_params['apikey'] = unicode(apikey)
         user = model.User(**user_params)


--- a/ckan/lib/create_test_data.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/lib/create_test_data.py	Mon Jul 18 09:57:42 2011 +0100
@@ -259,7 +259,7 @@
             changeset_ids = ChangesetRegister().commit()
 
     @classmethod
-    def create_groups(cls, group_dicts, admin_user_name):
+    def create_groups(cls, group_dicts, admin_user_name=None):
         '''A more featured interface for creating groups.
         All group fields can be filled, packages added and they can
         have an admin user.'''
@@ -267,13 +267,19 @@
         rev = model.repo.new_revision()
         # same name as user we create below
         rev.author = cls.author
-        admin_user = model.User.by_name(admin_user_name)
+        if admin_user_name:
+            admin_users = [model.User.by_name(admin_user_name)]
+        else:
+            admin_users = []
         assert isinstance(group_dicts, (list, tuple))
+        group_attributes = set(('name', 'title', 'description', 'parent_id'))
         for group_dict in group_dicts:
             group = model.Group(name=unicode(group_dict['name']))
-            for key in ('title', 'description'):
-                if group_dict.has_key(key):
+            for key in group_dict:
+                if key in group_attributes:
                     setattr(group, key, group_dict[key])
+                else:
+                    group.extras[key] = group_dict[key]
             pkg_names = group_dict.get('packages', [])
             assert isinstance(pkg_names, (list, tuple))
             for pkg_name in pkg_names:
@@ -281,7 +287,7 @@
                 assert pkg, pkg_name
                 pkg.groups.append(group)
             model.Session.add(group)
-            model.setup_default_user_roles(group, [admin_user])
+            model.setup_default_user_roles(group, admin_users)
             cls.group_names.add(group_dict['name'])
         model.repo.commit_and_remove()
 


--- a/ckan/lib/cswclient.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +0,0 @@
-#!/usr/bin/env python
-import urllib
-import urllib2
-import cookielib
-from lxml import etree
-
-class CswError(Exception): pass
-
-class CswRequest(object):
-
-    template = ""
-
-    def __init__(self, **kwds):
-        self.params = kwds
-
-    def get_params(self):
-        return self.params
-
-    def get_xml(self):
-        if self.template == None:
-            raise CswError, "No template attribute on class %s." % self.__class__
-        return self.template % self.get_params()
-
-
-class CswGetCapabilities(CswRequest):
-
-    template = """<?xml version="1.0"?>
-<csw:GetCapabilities xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW">
-    <ows:AcceptVersions xmlns:ows="http://www.opengis.net/ows">
-        <ows:Version>2.0.2</ows:Version>
-    </ows:AcceptVersions>
-    <ows:AcceptFormats xmlns:ows="http://www.opengis.net/ows">
-        <ows:OutputFormat>application/xml</ows:OutputFormat>
-    </ows:AcceptFormats>
-</csw:GetCapabilities>"""
-
-
-class CswGetRecords(CswRequest):
-
-    template = """<?xml version="1.0"?>
-<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
-    xmlns:gmd="http://www.isotc211.org/2005/gmd" service="CSW" version="2.0.2" resultType="%(result_type)s">
-    <csw:Query typeNames="gmd:MD_Metadata">
-        <csw:ElementName>dc:identifier</csw:ElementName>
-        <csw:Constraint version="1.1.0">
-            <Filter xmlns="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"/>
-        </csw:Constraint>
-    </csw:Query>
-</csw:GetRecords>"""
-
-    def __init__(self, result_type='results'):
-        super(CswGetRecords, self).__init__(result_type=result_type)
-
-
-class CswGetRecordById(CswRequest):
-
-    template = """<?xml version="1.0"?>
-<csw:GetRecordById xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2"
-    outputSchema="csw:IsoRecord">
-    <csw:Id>%(identifier)s</csw:Id>
-</csw:GetRecordById>"""
-
-    def __init__(self, identifier):
-        super(CswGetRecordById, self).__init__(identifier=identifier)
-
-
-class CswClient(object):
-
-    namespaces = {
-        "csw": "http://www.opengis.net/cat/csw/2.0.2",
-        "xsi": "http://www.w3.org/2001/XMLSchema-instance",
-        "geonet": "http://www.fao.org/geonetwork",
-        "dc": "http://purl.org/dc/elements/1.1/",
-        "dct": "http://purl.org/dc/terms/",
-        "gmd": "http://www.isotc211.org/2005/gmd",
-    }
-
-    def __init__(self, base_url, csw_uri='', login_uri='', logout_uri='', username=None, password=None):
-        self.base_url = base_url
-        self.csw_uri = csw_uri
-        self.login_uri = login_uri
-        self.logout_uri = logout_uri
-        self.username = username
-        self.password = password
-        self.login_url = self.base_url + self.login_uri
-        self.logout_url = self.base_url + self.logout_uri
-        self.csw_url = self.base_url + self.csw_uri
-        self.opener = None
-
-    def assert_capabilities(self):
-        xml = self.send_get_capabilities()
-        # Check document type is csw:Capabilities.
-        if "<csw:Capabilities" not in xml:
-            msg = "Doesn't look like a capabilities response: %s" % xml
-            raise CswError, msg
-        # Check service type is CSW.
-        if "<ows:ServiceType>CSW</ows:ServiceType>" not in xml:
-            msg = "Doesn't look like a CSW service: %s" % xml
-            raise CswError, msg
-        # Check is capable of GetRecords operation.
-        if "<ows:Operation name=\"GetRecords\">" not in xml:
-            msg = "Doesn't look like GetRecords operation is supported: %s" % xml
-            raise CswError, msg
-        # Check is capable of GetRecordById operation.
-        if "<ows:Operation name=\"GetRecordById\">" not in xml:
-            msg = "Doesn't look like GetRecordById operation is supported: %s" % xml
-            raise CswError, msg
-        # Todo: Change above code to use XPaths?
-
-    #def iter_records(self, max_records=None):
-    #    records = []
-    #    for id in self.get_identifiers():
-    #        record = self.get_record_by_id(id)
-    #        records.append(record)
-    #        if max_records and len(records) == max_records:
-    #            break
-    #    return records
-
-    def get_records(self, max_records=None):
-        records = []
-        for id in self.get_identifiers():
-            record = self.get_record_by_id(id)
-            records.append(record)
-            if max_records and len(records) == max_records:
-                break
-        return records
-
-    def get_identifiers(self):
-        response = self.send_get_records()
-        return self.extract_identifiers(response)
-       
-    def get_record_by_id(self, identifier):
-        response = self.send_get_record_by_id(identifier)
-        return self.extract_metadata(response)
-
-    def send_get_capabilities(self):
-        return self.send(CswGetCapabilities())
-
-    def send_get_records(self):
-        return self.send(CswGetRecords())
-       
-    def send_get_record_by_id(self, identifier): 
-        return self.send(CswGetRecordById(identifier=identifier))
-
-    def send(self, csw_request):
-        csw_request_xml = self.get_xml_from_csw_request(csw_request)
-        http_header = {"Content-type": "application/xml", "Accept": "text/plain"}
-        http_request = urllib2.Request(self.csw_url, csw_request_xml, http_header)
-        try:
-            http_response = self.urlopen(http_request)
-        except Exception, inst:
-            msg = "Couldn't send CSW request to CSW server: %s: %s: %s" % (
-                self.base_url, inst, csw_request_xml
-            )
-            raise CswError, msg 
-        csw_response_xml = http_response.read()
-        return csw_response_xml
-
-    def get_xml_from_csw_request(self, csw_request):
-        if isinstance(csw_request, CswRequest):
-            csw_request_xml = csw_request.get_xml()
-        else:
-            csw_request_xml = csw_request
-        return csw_request_xml
-
-    def extract_identifiers(self, get_records_response):
-        parser = etree.XMLParser(remove_blank_text=True)
-        tree = etree.fromstring(get_records_response, parser=parser)
-        xpath = '//csw:Record/dc:identifier/text()'
-        return tree.xpath(xpath, namespaces=self.namespaces)
-
-    def extract_metadata(self, get_record_by_id_response):
-        parser = etree.XMLParser(remove_blank_text=True)
-        tree = etree.fromstring(get_record_by_id_response, parser=parser)
-        xpath = 'gmd:MD_Metadata'
-        elems = tree.xpath(xpath, namespaces=self.namespaces)
-        if isinstance(elems, list) and len(elems) == 1:
-            elem = elems[0]
-        else:
-            msg = "Unexpected return value from etree.xpath: %s" % repr(elems)
-            raise CswError, msg
-        return etree.tostring(elem)
-
-    def login(self):
-        if not (self.username and self.password):
-            return
-        self.logout()
-        http_params = urllib.urlencode({"username": self.username, "password": self.password})
-        http_header = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
-        http_request = urllib2.Request(self.login_url, http_params, http_header)
-        try:
-            http_response = self.urlopen(http_request)
-        except Exception, inst:
-            msg = "Couldn't login to CSW with given credentials: %s" % inst
-            raise CswError, msg 
-        cookie_jar = cookielib.CookieJar()
-        cookie_jar.extract_cookies(http_response, http_request)
-        cookie_handler= urllib2.HTTPCookieProcessor(cookie_jar)
-        redirect_handler= urllib2.HTTPRedirectHandler()
-        self.opener = urllib2.build_opener(redirect_handler, cookie_handler)
-        
-    def logout(self):
-        if not (self.username and self.password):
-            return
-        http_request = urllib2.Request(self.logout_url)
-        try:
-            http_response = self.urlopen(http_request)
-        except Exception, inst:
-            msg = "Couldn't logout from CSW server %s: %s" % (
-                self.base_url, inst)
-            raise CswError, msg 
-        xml_response = http_response.read()
-        if "<ok />" not in xml_response:
-            msg = "Couldn't logout from CSW server %s: %s" % (
-                self.base_url, xml_response)
-            raise CswError, msg
-
-    def urlopen(self, http_request):
-        try:
-            if self.opener:
-                http_response = self.opener.open(http_request)
-            else:
-                http_response = urllib2.urlopen(http_request)
-        except urllib2.URLError, inst:
-            msg = 'Error making CSW server request: %s' % inst
-            raise CswError, msg
-        else:
-            return http_response
-
-
-class GeoNetworkClient(CswClient):
-
-    def __init__(self, base_url, username='', password=''):
-        login_uri = '/../xml.user.login'
-        logout_uri = '/../xml.user.logout'
-        super(GeoNetworkClient, self).__init__(
-            base_url=base_url,
-            login_uri=login_uri,
-            logout_uri=logout_uri,
-            username=username,
-            password=password,
-        )
-


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/lib/mailer.py	Mon Jul 18 09:57:42 2011 +0100
@@ -0,0 +1,86 @@
+import smtplib
+import logging
+import uuid
+from time import time
+from email.mime.text import MIMEText
+from email.header import Header
+from email import Utils
+from urlparse import urljoin
+
+from pylons.i18n.translation import _
+from pylons import config, g
+from ckan import model, __version__
+from ckan.lib.helpers import url_for
+
+log = logging.getLogger(__name__)
+
+class MailerException(Exception):
+    pass
+
+def _mail_recipient(recipient_name, recipient_email,
+        sender_name, sender_url, subject,
+        body, headers={}):
+    mail_from = config.get('ckan.mail_from')
+    body = _(u"Dear %s,") % recipient_name \
+         + u"\r\n\r\n%s\r\n\r\n" % body \
+         + u"--\r\n%s (%s)" % (sender_name, sender_url)
+    msg = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
+    for k, v in headers.items(): msg[k] = v
+    subject = Header(subject.encode('utf-8'), 'utf-8')
+    msg['Subject'] = subject
+    msg['From'] = _("%s <%s>") % (sender_name, mail_from)
+    recipient = u"%s <%s>" % (recipient_name, recipient_email)
+    msg['To'] = Header(recipient, 'utf-8')
+    msg['Date'] = Utils.formatdate(time())
+    msg['X-Mailer'] = "CKAN %s" % __version__
+    try:
+        server = smtplib.SMTP(config.get('smtp_server', 'localhost'))
+        #server.set_debuglevel(1)
+        server.sendmail(mail_from, [recipient_email], msg.as_string())
+        server.quit()
+    except Exception, e:
+        log.exception(e)
+        raise MailerException(e.message)
+
+def mail_recipient(recipient_name, recipient_email, subject, 
+        body, headers={}):
+    return _mail_recipient(recipient_name, recipient_email,
+            g.site_title, g.site_url, subject, body, headers=headers)
+
+def mail_user(recipient, subject, body, headers={}):
+    if (recipient.email is None) and len(recipient.email):
+        raise MailerException(_("No recipient email address available!"))
+    mail_recipient(recipient.display_name, recipient.email, subject, 
+            body, headers=headers)
+
+
+def make_key():
+    return uuid.uuid4().hex[:10]
+
+RESET_LINK_MESSAGE = _(
+'''You have requested your password on %(site_title)s to be reset.
+
+Please click the following link to confirm this request:
+
+   %(reset_link)s
+''')
+
+def send_reset_link(user):
+    user.reset_key = make_key()
+    model.Session.add(user)
+    model.Session.commit()
+    d = {
+        'reset_link': urljoin(g.site_url, url_for(controller='user',
+            action='perform_reset', id=user.id, key=user.reset_key)),
+        'site_title': g.site_title
+        }
+    body = RESET_LINK_MESSAGE % d
+    mail_user(user, _('Reset your password'), body)
+
+def verify_reset_link(user, key):
+    if not user.reset_key or len(user.reset_key) < 5:
+        return False
+    return key.strip() == user.reset_key
+
+
+


--- a/ckan/lib/monitor.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-from ckan.lib.async_notifier import AsyncConsumer
-
-class Monitor(AsyncConsumer):
-    '''Monitor for asynchronous notifications. Prints any notifications.
-    NB Doesn\'t work when carrot backend configured to use Python Queue - needs rabbitmq
-    or similar.
-    '''
-    def __init__(self):
-        queue_name = 'monitor'
-        routing_key = '*'
-        super(Monitor, self).__init__(queue_name, routing_key)
-        print 'Monitoring notifications'
-        print 'Options:'
-        options = self.consumer_options.items() + {'host':self.conn.host,
-                                                   'userid':self.conn.userid,
-                                                   'password':self.conn.password}.items()
-        for key, value in options:
-            print '    %s: %s' % (key, value)
-        print '...'
-        self.run()
-
-    def callback(self, notification):
-        print '%s: %r\n' % (notification.__class__.__name__, notification['payload'])


--- a/ckan/lib/navl/tests.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,389 +0,0 @@
-from dictization_functions import (flatten_schema,
-                                   get_all_key_combinations,
-                                   make_full_schema,
-                                   flatten_dict,
-                                   unflatten,
-                                   missing,
-                                   augment_data,
-                                   validate,
-                                   validate_flattened)
-from pprint import pprint, pformat
-from validators import (identity_converter,
-                        empty,
-                        not_empty,
-                        ignore_missing,
-                        default,
-                        convert_int,
-                        ignore)
-
-from formencode import validators
-
-
-schema = {
-    "__after": [identity_converter],
-    "__extra": [identity_converter],
-    "__junk": [identity_converter],
-    "0": [identity_converter],
-    "1": [identity_converter],
-    "2": {
-        "__before": [identity_converter],
-        "__after": [identity_converter],
-        "20": [identity_converter],
-        "22": [identity_converter],
-        "21": {
-            "210": [identity_converter],
-        },
-    },
-    "3": {
-        "30": [identity_converter],
-    }
-}
-
-data = {
-    ("0",): "0 value",
-    #key 1 missing
-    ("2", 0, "20"): "20 value 0",
-    #key 2,22 missing
-    ("2", 0, "21", 0, "210"): "210 value 0,0",
-    #key 3 missing subdict
-    ("2", 1, "20"): "20 value 1",
-    ("2", 1, "22"): "22 value 1",
-    ("2", 1, "21", 0, "210"): "210 value 1,0",
-    ("2", 1, "21", 1, "210"): "210 value 1,1",
-    ("2", 1, "21", 3, "210"): "210 value 1,3", ##out of order sequence
-    ("4", 1, "30"): "30 value 1", #junk key as no 4 and no subdict
-    ("4",): "4 value", #extra key 4
-    ("2", 2, "21", 0, "210"): "210 value 2,0" #junk key as it does not have a parent
-}
-
-
-def test_flatten_schema():
-
-    flattened_schema = flatten_schema(schema)
-
-    assert flattened_schema == {
-         ('0',): [identity_converter],
-         ('1',): [identity_converter],
-         ('2', '20'): [identity_converter],
-         ('2', '__after'): [identity_converter],
-         ('2', '__before'): [identity_converter],
-         ('2', '21', '210'): [identity_converter],
-         ('2', '22'): [identity_converter],
-         ('3', '30'): [identity_converter],
-         ('__after',): [identity_converter],
-         ('__extra',): [identity_converter],
-         ('__junk',): [identity_converter],
-    }, pprint(flattened_schema)
-
-def test_get_key_combination():
-
-    flattened_schema = flatten_schema(schema)
-    assert get_all_key_combinations(data, flattened_schema) ==\
-        set([(),
-            ('2', 0), 
-            ('2', 1), 
-            ('2', 1, '21', 0),
-            ('2', 0, '21', 0),
-            ('2', 1, '21', 1), 
-            ('2', 1, '21', 3), 
-            ]), get_all_key_combinations(data, flattened_schema)
-
-    #state = {}
-    #make_flattened_schema(data, schema, state)
-
-def test_make_full_schema():
-
-    full_schema = make_full_schema(data, schema)
-
-    print set(full_schema.keys()) - set(data.keys())
-
-    assert set(full_schema.keys()) - set(data.keys()) == set([('2', 1, '__before'),
-                                                              ('2', 0, '__after'),
-                                                              ('2', 0, '22'),
-                                                              ('1',),
-                                                              ('2', 1, '__after'),
-                                                              ('2', 0, '__before'),
-                                                              ('__after',),
-                                                              ('__extra',),
-                                                              ('__junk',),
-                                                             ])
-
-    print set(data.keys()) - set(full_schema.keys())
-
-    assert set(data.keys()) - set(full_schema.keys()) == set([('2', 2, '21', 0, '210'),
-                                                              ('4',),
-                                                              ('4', 1, '30')])
-
-
-def test_augment_junk_and_extras():
-
-    assert augment_data(data, schema) == {
-         ('__junk',): {('2', 2, '21', 0, '210'): '210 value 2,0',
-                       ('4', 1, '30'): '30 value 1'},
-         ('0',): '0 value',
-         ('1',): missing,
-         ('2', 0, '20'): '20 value 0',
-         ('2', 0, '21', 0, '210'): '210 value 0,0',
-         ('2', 0, '22'): missing,
-         ('2', 1, '20'): '20 value 1',
-         ('2', 1, '21', 0, '210'): '210 value 1,0',
-         ('2', 1, '21', 1, '210'): '210 value 1,1',
-         ('2', 1, '21', 3, '210'): '210 value 1,3',
-         ('2', 1, '22'): '22 value 1',
-         ('__extras',): {'4': '4 value'}}, pprint(augment_data(data, schema))
-
-
-def test_identity_validation():
-
-    
-    converted_data, errors = validate_flattened(data, schema)
-    print errors
-
-    assert not errors
-
-
-    assert sorted(converted_data) == sorted({
-         ('__junk',): {('2', 2, '21', 0, '210'): '210 value 2,0',
-                       ('4', 1, '30'): '30 value 1'},
-         ('0',): '0 value',
-         ('1',): missing,
-         ('2', 0, '20'): '20 value 0',
-         ('2', 0, '21', 0, '210'): '210 value 0,0',
-         ('2', 0, '22'): missing,
-         ('2', 1, '20'): '20 value 1',
-         ('2', 1, '21', 0, '210'): '210 value 1,0',
-         ('2', 1, '21', 1, '210'): '210 value 1,1',
-         ('2', 1, '21', 3, '210'): '210 value 1,3',
-         ('2', 1, '22'): '22 value 1',
-         ('__extras',): {'4': '4 value'}}), pprint(sorted(converted_data))
-
-
-def test_basic_errors():
-    schema = {
-        "__junk": [empty],
-        "__extras": [empty],
-        "0": [identity_converter],
-        "1": [not_empty],
-        "2": {
-            "__before": [identity_converter],
-            "__after": [identity_converter],
-            "20": [identity_converter],
-            "22": [identity_converter],
-            "__extras": [empty],
-            "21": {
-                "210": [identity_converter],
-            },
-        },
-        "3": {
-            "30": [identity_converter],
-        },
-    }
-
-    converted_data, errors = validate_flattened(data, schema)
-
-    assert errors == {('__junk',): [u'The input field __junk was not expected.'], ('1',): [u'Missing value'], ('__extras',): [u'The input field __extras was not expected.']}, errors
-
-def test_default():
-    schema = {
-        "__junk": [ignore],
-        "__extras": [ignore, default("weee")],
-        "__before": [ignore],
-        "__after": [ignore],
-        "0": [default("default")],
-        "1": [default("default")],
-    }
-
-    converted_data, errors = validate_flattened(data, schema)
-
-    assert not errors
-    assert converted_data == {('1',): 'default', ('0',): '0 value'}, converted_data
-
-
-def test_ignore_missing():
-    schema = {
-        "__junk": [ignore],
-        "__extras": [ignore, default("weee")],
-        "0": [default("default")],
-        "1": [ignore_missing, default("default")],
-    }
-
-    converted_data, errors = validate_flattened(data, schema)
-
-    assert not errors
-    assert converted_data == {('0',): '0 value'}, converted_data
-
-
-def test_flatten():
-
-    data = {'extras': [{'key': 'genre', 'value': u'horror'},
-                       {'key': 'media', 'value': u'dvd'}],
-            'license_id': u'gpl-3.0',
-            'name': u'testpkg',
-            'resources': [{u'alt_url': u'alt_url',
-                          u'description': u'Second file',
-                          u'extras': {u'size': u'200'},
-                          u'format': u'xml',
-                          u'hash': u'def123',
-                          u'url': u'http://blah.com/file2.xml'},
-                          {u'alt_url': u'alt_url',
-                          u'description': u'Main file',
-                          u'extras': {u'size': u'200'},
-                          u'format': u'xml',
-                          u'hash': u'abc123',
-                          u'url': u'http://blah.com/file.xml'}],
-            'tags': [{'name': u'russion'}, {'name': u'novel'}],
-            'title': u'Some Title',
-            'url': u'http://blahblahblah.mydomain'}
-
-    assert flatten_dict(data) == {('extras', 0, 'key'): 'genre',
-                                 ('extras', 0, 'value'): u'horror',
-                                 ('extras', 1, 'key'): 'media',
-                                 ('extras', 1, 'value'): u'dvd',
-                                 ('license_id',): u'gpl-3.0',
-                                 ('name',): u'testpkg',
-                                 ('resources', 0, u'alt_url'): u'alt_url',
-                                 ('resources', 0, u'description'): u'Second file',
-                                 ('resources', 0, u'extras'): {u'size': u'200'},
-                                 ('resources', 0, u'format'): u'xml',
-                                 ('resources', 0, u'hash'): u'def123',
-                                 ('resources', 0, u'url'): u'http://blah.com/file2.xml',
-                                 ('resources', 1, u'alt_url'): u'alt_url',
-                                 ('resources', 1, u'description'): u'Main file',
-                                 ('resources', 1, u'extras'): {u'size': u'200'},
-                                 ('resources', 1, u'format'): u'xml',
-                                 ('resources', 1, u'hash'): u'abc123',
-                                 ('resources', 1, u'url'): u'http://blah.com/file.xml',
-                                 ('tags', 0, 'name'): u'russion',
-                                 ('tags', 1, 'name'): u'novel',
-                                 ('title',): u'Some Title',
-                                 ('url',): u'http://blahblahblah.mydomain'}, pformat(flatten_dict(data))
-
-    assert data == unflatten(flatten_dict(data))
-
-
-def test_simple():
-    schema = {
-        "name": [not_empty],
-        "age": [ignore_missing, convert_int],
-        "gender": [default("female")],
-    }
-
-    data = {
-        "name": "fred",
-        "age": "32",
-    }
-
-
-    converted_data, errors = validate(data, schema)
-
-    assert not errors
-    assert converted_data == {'gender': 'female', 'age': 32, 'name': 'fred'}, converted_data
-
-    data = {
-        "name": "",
-        "age": "dsa32",
-        "extra": "extra",
-    }
-
-    converted_data, errors = validate(data, schema)
-
-    assert errors == {('age',): [u'Please enter an integer value'], ('name',): [u'Missing value']}, errors
-
-    assert converted_data == {'gender': 'female', 'age': 'dsa32', 'name': '', '__extras': {'extra': 'extra'}}
-
-
-    data = {"name": "fred",
-            "numbers": [{"number": "13221312"},
-                        {"number": "432423432", "code": "+44"}]
-            }
-
-    schema = {
-           "name": [not_empty],
-           "numbers": {
-               "number": [convert_int],
-               "code": [not_empty],
-               "__extras": [ignore],
-           }
-        }
-
-    converted_data, errors = validate(data, schema)
-
-    assert errors == {('numbers', 0, 'code'): ['Missing value']}
-
-
-def test_simple_converter_types():
-    schema = {
-        "name": [not_empty, unicode],
-        "age": [ignore_missing, int],
-        "gender": [default("female")],
-    }
-
-    data = {
-        "name": "fred",
-        "age": "32",
-    }
-
-    converted_data, errors = validate(data, schema)
-    assert not errors
-    assert converted_data == {'gender': 'female', 'age': 32, 'name': u'fred'}, converted_data
-
-    assert isinstance(converted_data["name"], unicode)
-    assert not isinstance(converted_data["gender"], unicode)
-
-
-def test_formencode_compat():
-    schema = {
-        "name": [not_empty, unicode],
-        "email": [validators.Email],
-        "email2": [validators.Email],
-    }
-
-    data = {
-        "name": "fred",
-        "email": "32",
-        "email2": "david at david.com",
-    }
-
-    converted_data, errors = validate(data, schema)
-    assert errors == {('email',): [u'An email address must contain a single @']}, errors
-
-def test_range_validator():
-
-    schema = {
-        "name": [not_empty, unicode],
-        "email": [validators.Int(min=1, max=10)],
-        "email2": [validators.Email],
-    }
-
-    data = {
-        "email": "32",
-        "email2": "david at david.com",
-    }
-
-    converted_data, errors = validate(data, schema)
-    assert errors == {('name',): [u'Missing value'], ('email',): [u'Please enter a number that is 10 or smaller']}, errors
-
-def test_zz_formencode_translation():
-
-    schema = {
-        "name": [not_empty, unicode],
-        "email": [validators.Email],
-        "email2": [validators.Email],
-    }
-
-    data = {
-        "email": "32",
-        "email2": "david at david.com",
-    }
-
-    import formencode
-    formencode.api.set_stdtranslation(languages = ['de'])
-    import ckan
-
-    converted_data, errors = validate(data, schema)
-    assert errors == {('name',): [u'Fehlender Wert'], ('email',): [u'Eine E-Mail-Adresse muss genau ein @-Zeichen enthalten']}, errors
-
-
-
-
-


--- a/ckan/lib/rdf.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-import surf
-
-DOMAIN = 'http://ckan.net'
-CKAN_NAMESPACE = 'http://ckan.net/#'
-CKAN_SUBJECT_BASE = DOMAIN + '/package/'
-
-class RdfExporter(object):
-    def __init__(self):
-        surf.ns.register(ckan=CKAN_NAMESPACE)
-        surf.ns.register(dc='http://purl.org/dc/elements/1.1/') # old, allows literals
-        #surf.ns.register(dc='http://purl.org/dc/terms/') # new one
-
-    def export_package(self, pkg):
-        assert isinstance(pkg, dict)
-        
-        store = surf.Store(reader='rdflib',
-                           writer='rdflib',
-                           rdflib_store='IOMemory')
-
-        self._rdf_session = surf.Session(store)
-        
-        PackageRdf = self._web_resource(surf.ns.CKAN['Package'])[0]
-
-        pkg_rdf = PackageRdf(subject=CKAN_SUBJECT_BASE + pkg['name'])
-
-        pkg_rdf.foaf_isPrimaryTopicOf = self._web_resource(DOMAIN + '/package/read/%s' % pkg['name'])
-
-
-        if pkg['download_url']:
-            pkg_rdf.ckan_downloadUrl = self._web_resource(pkg['download_url'])
-
-        if pkg['title']:
-            pkg_rdf.dc_title = pkg['title']
-
-        if pkg['url']:
-            pkg_rdf.foaf_homepage = self._web_resource(pkg['url'])
-
-        if pkg['tags']:
-            pkg_rdf.dc_subject = pkg['tags']
-
-        if pkg['author'] or pkg['author_email']:
-##            pkg_rdf.dc_creator = pkg['author'].strip()
-##            if pkg['author_email']:
-##                pkg_rdf.dc_creator = [pkg['author'].strip(), self._web_resource('mailto:' + pkg['author_email'])]
-            pkg_rdf.dc_creator = ((pkg['author'] or '') + ' ' + (pkg['author_email'] or '')).strip()
-
-        if pkg['maintainer'] or pkg['maintainer_email']:
-            pkg_rdf.dc_contributor = ((pkg['maintainer'] or '') + ' ' + (pkg['maintainer_email'] or '')).strip()
-
-        if pkg['license']:
-            pkg_rdf.dc_rights = pkg['license']
-
-        if pkg['notes']:
-            pkg_rdf.dc_description = pkg['notes']
-
-        self._rdf_session.commit()
-
-        return pkg_rdf.serialize()
-    
-    def _web_resource(self, url):
-        # cope with multiple urls in string
-        url = url.replace(',', ' ')
-        urls = url.split()
-
-        url_classes = []
-        for url in urls:
-            try:
-                url_classes.append(self._rdf_session.get_class(url))
-            except ValueError, e:
-                url_classes.append(url)
-
-        return url_classes
-


--- a/ckan/lib/search/common.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/lib/search/common.py	Mon Jul 18 09:57:42 2011 +0100
@@ -146,7 +146,10 @@
     def validate(self):
         for key, value in self.items():
             if key in self.BOOLEAN_OPTIONS:
-                value = asbool(value)
+                try:
+                    value = asbool(value)
+                except ValueError:
+                    raise SearchError('Value for search option %r must be True or False (1 or 0) but received %r' % (key, value))
             elif key in self.INTEGER_OPTIONS:
                 try:
                     value = int(value)


--- a/ckan/lib/talis.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-import urllib2
-import os
-
-import rdf
-
-TALIS_REALM = 'bigfoot'
-TALIS_URI = 'http://api.talis.com/stores/'
-
-class TalisLogin:
-    storename = 'ckan-dev1'
-    username = 'ckan'
-    password = None
-
-    @staticmethod
-    def init(storename, username, password):
-        TalisLogin.storename = storename
-        TalisLogin.username = username
-        TalisLogin.password = password
-
-    @staticmethod
-    def get_password():
-        if TalisLogin.password is None:
-            TalisLogin.password = os.environ['TALIS_PASSWORD']
-        return TalisLogin.password
-
-    @staticmethod
-    def get_username():
-        return TalisLogin.username
-
-    @staticmethod
-    def get_storename():
-        return TalisLogin.storename
-
-class Talis:
-    def __init__(self):
-        self.rdf = rdf.RdfExporter()
-
-    def get_field_predicate_map(self, store):
-        fp_map_base = \
-'''
-<?xml version="1.0" encoding="utf-8"?>
-<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:bf="http://schemas.talis.com/2006/bigfoot/configuration#" xmlns:frm="http://schemas.talis.com/2006/frame/schema#" >
-  <bf:FieldPredicateMap rdf:about="http://api.talis.com/stores/$(store)s/indexes/default/fpmaps/default">
-    %(mapped_properties)s
-  </bf:FieldPredicateMap>
-</rdf:RDF>
-'''
-
-        mapped_property = \
-'''
-    <frm:mappedDatatypeProperty>
-      <rdf:Description rdf:about="http://api.talis.com/stores/$(store)s/indexes/default/fpmaps/default#%(property)s">
-        <frm:property rdf:resource="%(namespace)s%(property)s"/>
-        <frm:name>%(property)</frm:name>
-      </rdf:Description>
-    </frm:mappedDatatypeProperty>
-'''
-        namespaces = {'dc':'http://purl.org/dc/elements/1.1/',
-                      'foaf':'http://xmlns.com/foaf/0.1/',
-                      'ckan':'http://ckan.net/ns#',
-        }
-        map = [('isPrimaryTopicOf', 'foaf'),
-        ('title', 'dc'),
-        ('description', 'dc'),
-        ('foaf', 'homepage'),
-        ('downloadUrl', 'ckan'),
-        ('contributor', 'dc'),
-        ('rights', 'dc'),
-        ('subject', 'dc'),
-        ]
-
-        mapped_properties = []
-        for property, ns_abbreviation in map:
-            mp = mapped_property % {'namespace':namespaces[ns_abbreviation],
-            'property':property}
-            mapped_properties.append(mp)
-        return fp_map_base % {'mapped_properties':'\n'.join(mapped_properties), 'store': store}
-
-    def post_pkg(self, pkg_dict):
-        rdf_xml = self.rdf.export_package(pkg_dict)
-        service = '/meta'
-        return self.post(service, rdf_xml, with_password=True)
-    
-    def post(self, service, rdf_xml, with_password=False):
-        uri = TALIS_URI + TalisLogin.get_storename() + service
-        if with_password:
-            talis_password = TalisLogin.get_password()
-            talis_username = TalisLogin.get_username()
-            authhandler = urllib2.HTTPDigestAuthHandler()
-            authhandler.add_password(TALIS_REALM, uri, talis_username, talis_password)
-            opener = urllib2.build_opener(authhandler)
-            urllib2.install_opener(opener)        
-        headers = {"User-Agent":"ckan-rdf", "Content-Type":"application/rdf+xml"}
-        request = urllib2.Request(uri, rdf_xml, headers)
-        try:
-            response = urllib2.urlopen(request)
-        except urllib2.HTTPError, arg:
-            return arg
-        return response.read()
-
-    def get_pkg(self, package_name):
-        uri = TALIS_URI + TalisLogin.get_storename() + '/meta?about=%s&output=rdf' % (rdf.CKAN_SUBJECT_BASE + package_name)
-        return self.get(uri)
-        
-    def get_pkg_list(self):
-        uri = TALIS_URI + TalisLogin.get_storename() + '/meta?about=%s&output=rdf' % (rdf.CKAN_SUBJECT_BASE)
-        return self.get(uri)        
-
-    def get(self, uri):
-        headers = {"User-Agent":"ckan-rdf"}
-        request = urllib2.Request(uri, None, headers)
-        try:
-            response = urllib2.urlopen(request)
-        except urllib2.HTTPError, arg:
-            return arg
-        return response.read()
-        
-    def send_rdf(self, talis_store, username, password):
-        import ckan.model as model
-        TalisLogin.storename = talis_store
-        TalisLogin.username = username
-        TalisLogin.password = password
-
-        err = None
-        for pkg in model.Session.query(model.Package).all():
-            pkg_dict = pkg.as_dict()
-            err = self.post_pkg(pkg_dict)
-            if err:
-                print "error"
-                print err
-                break
-        return err
-
-        
-
-    def dump(self):
-        pass


--- a/ckan/migration/versions/029_version_groups.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/migration/versions/029_version_groups.py	Mon Jul 18 09:57:42 2011 +0100
@@ -147,7 +147,7 @@
     package_group_revision_table.create()
     for row in migrate_engine.execute(package_group_table.select()):
         pkg_group_rev = dict(row.items())
-        pkg_group_rev['continuity_id'] = group_rev['id']
+        pkg_group_rev['continuity_id'] = pkg_group_rev['id']
         q = package_group_revision_table.insert(values=pkg_group_rev)
         migrate_engine.execute(q)
     


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/migration/versions/040_reset_key_on_user.py	Mon Jul 18 09:57:42 2011 +0100
@@ -0,0 +1,13 @@
+from sqlalchemy import *
+from migrate import *
+
+def upgrade(migrate_engine):
+    metadata = MetaData()
+    metadata.bind = migrate_engine
+    user_table = Table('user', metadata, autoload=True)
+    reset_key_col = Column('reset_key', UnicodeText)
+    reset_key_col.create(user_table)
+
+def downgrade(migrate_engine):
+    raise NotImplementedError()
+


--- a/ckan/model/group.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/model/group.py	Mon Jul 18 09:57:42 2011 +0100
@@ -1,3 +1,5 @@
+import datetime
+
 from meta import *
 from core import *
 from sqlalchemy.orm import eagerload_all


--- a/ckan/model/rating.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/model/rating.py	Mon Jul 18 09:57:42 2011 +0100
@@ -1,3 +1,5 @@
+import datetime
+
 from meta import *
 from core import *
 from package import *


--- a/ckan/model/user.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/model/user.py	Mon Jul 18 09:57:42 2011 +0100
@@ -18,6 +18,7 @@
         Column('email', UnicodeText),
         Column('apikey', UnicodeText, default=make_uuid),
         Column('created', DateTime, default=datetime.datetime.now),
+        Column('reset_key', UnicodeText),
         Column('about', UnicodeText),
         )
 


--- a/ckan/public/scripts/flexitable.js	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/public/scripts/flexitable.js	Mon Jul 18 09:57:42 2011 +0100
@@ -62,7 +62,12 @@
     if (confirm('Are you sure you wish to remove this row?')) {
       var row = $(this).parents('tr'),
           following = row.nextAll();
-      
+          prev = row.prevAll();
+
+      if (following.length + prev.length  == 0) {
+          row.find('input').val('')
+          return
+      }
       row.remove();
       following.each(function () {
         setRowNumber(this, getRowNumber(this) - 1);


--- a/ckan/templates/user/login.html	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/templates/user/login.html	Mon Jul 18 09:57:42 2011 +0100
@@ -23,6 +23,7 @@
       </p><ul><li>${h.link_to(_('Register'), h.url_for(action='register'))}</li>
+        <li>${h.link_to(_('Reset your password'), h.url_for(action='request_reset'))}</li><li>${h.link_to(_('Privacy Policy'), 'http://www.okfn.org/privacy-policy/')}</li></ul></li>
@@ -44,7 +45,8 @@
         <input type="password" name="password" value="" /><br/></fieldset>
-      ${h.submit('s', _('Login'))}
+      ${h.submit('s', _('Login'))} — 
+      <a href="${h.url_for('reset')}">Forgot your password?</a></form><!-- Simple OpenID Selector -->


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/templates/user/perform_reset.html	Mon Jul 18 09:57:42 2011 +0100
@@ -0,0 +1,34 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+  xmlns:i18n="http://genshi.edgewall.org/i18n"
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip="">
+  
+  <py:def function="page_title">Reset your password</py:def>
+
+  <py:def function="optional_head">
+    <link rel="stylesheet" href="${g.site_url}/css/forms.css" type="text/css" media="screen, print" />
+  </py:def>
+
+  <div py:match="content">
+    <form id="user-reset" action="" method="post" class="simple-form" 
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      >
+      <fieldset>
+        <legend>Reset 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>
+      <div>
+        ${h.submit('save', _('Save'))}
+      </div>
+    </form>
+  </div>
+
+  <xi:include href="layout.html" />
+</html>
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/templates/user/request_reset.html	Mon Jul 18 09:57:42 2011 +0100
@@ -0,0 +1,35 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+  xmlns:i18n="http://genshi.edgewall.org/i18n"
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip="">
+  
+  <py:def function="page_title">Reset password</py:def>
+
+  <py:def function="optional_head">
+    <link rel="stylesheet" href="${g.site_url}/css/forms.css" type="text/css" media="screen, print" />
+  </py:def>
+
+  <div py:match="content">
+    <h2>
+      Request a password reset
+    </h2>
+
+    <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>Reset password</legend>
+        <label for="user">User name:</label>
+        <input name="user" value="" /><br/>
+      </fieldset>
+
+      <div>
+        ${h.submit('save', _('Reset password'))}
+      </div>
+    </form>
+  </div>
+
+  <xi:include href="layout.html" />
+</html>
+


--- a/ckan/tests/functional/api/model/test_package.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/tests/functional/api/model/test_package.py	Mon Jul 18 09:57:42 2011 +0100
@@ -118,13 +118,20 @@
         assert not self.get_package_by_name(self.package_fixture_data['name'])
         offset = self.package_offset()
         data = self.dumps(self.package_fixture_data)
-        res = self.http_request(offset, data, content_type='something/unheard_of',
-                                status=self.STATUS_400_BAD_REQUEST,
+        res = self.http_request(offset, data,
+                                content_type='something/unheard_of',
+                                status=[self.STATUS_400_BAD_REQUEST,
+                                        self.STATUS_201_CREATED],
                                 extra_environ=self.extra_environ)
-        # Check there is no database record.
         self.remove()
+        # Some versions of webob work, some don't. No matter, we record this
+        # behaviour.
         package = self.get_package_by_name(self.package_fixture_data['name'])
-        assert not package
+        if res.status == self.STATUS_400_BAD_REQUEST:
+            # Check there is no database record.
+            assert not package
+        else:
+            assert package        
 
     def test_register_post_json(self):
         assert not self.get_package_by_name(self.package_fixture_data['name'])


--- a/ckan/tests/functional/api/test_package_search.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/tests/functional/api/test_package_search.py	Mon Jul 18 09:57:42 2011 +0100
@@ -207,6 +207,13 @@
         assert anna_rec['ratings_average'] == 3.0, anna_rec['ratings_average']
         assert anna_rec['ratings_count'] == 1, anna_rec['ratings_count']
 
+    def test_08_all_fields_syntax_error(self):
+        offset = self.base_url + '?all_fields=should_be_boolean' # invalid all_fields value
+        res = self.app.get(offset, status=400)
+        assert('boolean' in res.body)
+        assert('all_fields' in res.body)
+        self.assert_json_response(res, 'boolean')
+
     def test_09_just_tags(self):
         offset = self.base_url + '?tags=russian&all_fields=1'
         res = self.app.get(offset, status=200)


--- a/ckan/tests/html_check.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/tests/html_check.py	Mon Jul 18 09:57:42 2011 +0100
@@ -10,7 +10,7 @@
     
     def named_div(self, div_name, html):
         'strips html to just the <div id="DIV_NAME"> section'
-        the_html = html.body.decode('utf8')
+        the_html = self._get_html_from_res(html)
         start_div = the_html.find(u'<div id="%s"' % div_name)
         end_div = the_html.find(u'<!-- #%s -->' % div_name)
         if end_div == -1:
@@ -94,7 +94,7 @@
         if partly_matching_tags:
             assert 0, "Couldn't find %s in html. Closest matches were:\n%s" % (', '.join(["'%s'" % html.encode('utf8') for html in html_to_find]), '\n'.join([tag.encode('utf8') for tag in partly_matching_tags]))
         else:
-            assert 0, "Couldn't find %s in html. Tags matched were:\n%s" % (', '.join(["'%s'" % html.encode('utf8') for html in html_to_find]), '\n'.join([tag.encode('utf8') for tag in regex_compiled.finditer(html_str)]))
+            assert 0, "Couldn't find %s in html. Tags matched were:\n%s" % (', '.join(["'%s'" % html.encode('utf8') for html in html_to_find]), '\n'.join([tag.group() for tag in regex_compiled.finditer(html_str)]))
 
 
 


--- a/ckan/tests/lib/mock_cswclient.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1007 +0,0 @@
-from ckan.lib.cswclient import GeoNetworkClient
-
-class MockGeoNetworkClient(GeoNetworkClient):
-
-    get_capabilities_request_xml = """<?xml version="1.0"?>
-<csw:GetCapabilities xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW">
-    <ows:AcceptVersions xmlns:ows="http://www.opengis.net/ows">
-        <ows:Version>2.0.2</ows:Version>
-    </ows:AcceptVersions>
-    <ows:AcceptFormats xmlns:ows="http://www.opengis.net/ows">
-        <ows:OutputFormat>application/xml</ows:OutputFormat>
-    </ows:AcceptFormats>
-</csw:GetCapabilities>"""
-
-    get_capabilities_response_xml = """
-<?xml version="1.0" encoding="UTF-8"?>
-<csw:Capabilities xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" xmlns:gml="http://www.opengis.net/gml" xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:ows="http://www.opengis.net/ows" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0.2" xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
-  <ows:ServiceIdentification>
-    <ows:Title />
-    <ows:Abstract />
-    <ows:Keywords>
-      <!-- Keywords are automatically added by GeoNetwork
-            according to catalogue content. -->
-      <ows:Type>theme</ows:Type>
-    </ows:Keywords>
-    <ows:ServiceType>CSW</ows:ServiceType>
-    <ows:ServiceTypeVersion>2.0.2</ows:ServiceTypeVersion>
-    <ows:Fees />
-    <ows:AccessConstraints />
-  </ows:ServiceIdentification>
-  <ows:ServiceProvider>
-    <ows:ProviderName>GeoNetwork opensource</ows:ProviderName>
-    <ows:ProviderSite xlink:href="http://localhost:8080/geonetwork" />
-    <ows:ServiceContact>
-      <ows:IndividualName />
-      <ows:PositionName />
-      <ows:ContactInfo>
-        <ows:Phone>
-          <ows:Voice />
-          <ows:Facsimile />
-        </ows:Phone>
-        <ows:Address>
-          <ows:DeliveryPoint />
-          <ows:City />
-          <ows:AdministrativeArea />
-          <ows:PostalCode />
-          <ows:Country />
-          <ows:ElectronicMailAddress />
-        </ows:Address>
-        <ows:HoursOfService />
-        <ows:ContactInstructions />
-      </ows:ContactInfo>
-      <ows:Role />
-    </ows:ServiceContact>
-  </ows:ServiceProvider>
-  <ows:OperationsMetadata>
-    <ows:Operation name="GetCapabilities">
-      <ows:DCP>
-        <ows:HTTP>
-          <ows:Get xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-          <ows:Post xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-        </ows:HTTP>
-      </ows:DCP>
-      <ows:Parameter name="sections">
-        <ows:Value>ServiceIdentification</ows:Value>
-        <ows:Value>ServiceProvider</ows:Value>
-        <ows:Value>OperationsMetadata</ows:Value>
-        <ows:Value>Filter_Capabilities</ows:Value>
-      </ows:Parameter>
-      <ows:Constraint name="PostEncoding">
-        <ows:Value>XML</ows:Value>
-      </ows:Constraint>
-    </ows:Operation>
-    <ows:Operation name="DescribeRecord">
-      <ows:DCP>
-        <ows:HTTP>
-          <ows:Get xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-          <ows:Post xlink:href="http://localhost:8080/geonetwork/srv/en/csw">
-            <ows:Constraint name="PostEncoding">
-              <ows:Value>XML</ows:Value>
-              <ows:Value>SOAP</ows:Value>
-            </ows:Constraint>
-          </ows:Post>
-        </ows:HTTP>
-      </ows:DCP>
-      <ows:Parameter name="typeName">
-        <ows:Value>csw:Record</ows:Value>
-        <ows:Value>gmd:MD_Metadata</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="outputFormat">
-        <ows:Value>application/xml</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="schemaLanguage">
-        <ows:Value>http://www.w3.org/TR/xmlschema-1/</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="typeName">
-        <ows:Value>csw:Record</ows:Value>
-        <ows:Value>gmd:MD_Metadata</ows:Value>
-      </ows:Parameter>
-      <ows:Constraint name="PostEncoding">
-        <ows:Value>XML</ows:Value>
-      </ows:Constraint>
-    </ows:Operation>
-    <ows:Operation name="GetDomain">
-      <ows:DCP>
-        <ows:HTTP>
-          <ows:Get xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-          <ows:Post xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-        </ows:HTTP>
-      </ows:DCP>
-    </ows:Operation>
-    <ows:Operation name="GetRecords">
-      <ows:DCP>
-        <ows:HTTP>
-          <ows:Get xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-          <ows:Post xlink:href="http://localhost:8080/geonetwork/srv/en/csw">
-            <ows:Constraint name="PostEncoding">
-              <ows:Value>XML</ows:Value>
-              <ows:Value>SOAP</ows:Value>
-            </ows:Constraint>
-          </ows:Post>
-        </ows:HTTP>
-      </ows:DCP>
-      <!-- FIXME : Gets it from enum or conf -->
-      <ows:Parameter name="resultType">
-        <ows:Value>hits</ows:Value>
-        <ows:Value>results</ows:Value>
-        <ows:Value>validate</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="outputFormat">
-        <ows:Value>application/xml</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="outputSchema">
-        <ows:Value>http://www.opengis.net/cat/csw/2.0.2</ows:Value>
-        <ows:Value>http://www.isotc211.org/2005/gmd</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="typeNames">
-        <ows:Value>csw:Record</ows:Value>
-        <ows:Value>gmd:MD_Metadata</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="CONSTRAINTLANGUAGE">
-        <ows:Value>FILTER</ows:Value>
-        <ows:Value>CQL_TEXT</ows:Value>
-      </ows:Parameter>
-      <ows:Constraint name="PostEncoding">
-        <ows:Value>XML</ows:Value>
-      </ows:Constraint>
-      <ows:Constraint name="SupportedISOQueryables">
-        <ows:Value>Operation</ows:Value>
-        <ows:Value>Format</ows:Value>
-        <ows:Value>OrganisationName</ows:Value>
-        <ows:Value>Type</ows:Value>
-        <ows:Value>ServiceType</ows:Value>
-        <ows:Value>DistanceValue</ows:Value>
-        <ows:Value>ResourceLanguage</ows:Value>
-        <ows:Value>RevisionDate</ows:Value>
-        <ows:Value>OperatesOn</ows:Value>
-        <ows:Value>GeographicDescriptionCode</ows:Value>
-        <ows:Value>AnyText</ows:Value>
-        <ows:Value>Modified</ows:Value>
-        <ows:Value>PublicationDate</ows:Value>
-        <ows:Value>ResourceIdentifier</ows:Value>
-        <ows:Value>ParentIdentifier</ows:Value>
-        <ows:Value>Identifier</ows:Value>
-        <ows:Value>CouplingType</ows:Value>
-        <ows:Value>TopicCategory</ows:Value>
-        <ows:Value>OperatesOnIdentifier</ows:Value>
-        <ows:Value>ServiceTypeVersion</ows:Value>
-        <ows:Value>TempExtent_end</ows:Value>
-        <ows:Value>Subject</ows:Value>
-        <ows:Value>CreationDate</ows:Value>
-        <ows:Value>OperatesOnName</ows:Value>
-        <ows:Value>Title</ows:Value>
-        <ows:Value>DistanceUOM</ows:Value>
-        <ows:Value>Denominator</ows:Value>
-        <ows:Value>AlternateTitle</ows:Value>
-        <ows:Value>Language</ows:Value>
-        <ows:Value>TempExtent_begin</ows:Value>
-        <ows:Value>HasSecurityConstraints</ows:Value>
-        <ows:Value>KeywordType</ows:Value>
-        <ows:Value>Abstract</ows:Value>
-      </ows:Constraint>
-      <ows:Constraint name="AdditionalQueryables">
-        <ows:Value>SpecificationDate</ows:Value>
-        <ows:Value>ConditionApplyingToAccessAndUse</ows:Value>
-        <ows:Value>AccessConstraints</ows:Value>
-        <ows:Value>OnlineResourceMimeType</ows:Value>
-        <ows:Value>MetadataPointOfContact</ows:Value>
-        <ows:Value>SpecificationDateType</ows:Value>
-        <ows:Value>Classification</ows:Value>
-        <ows:Value>OtherConstraints</ows:Value>
-        <ows:Value>OnlineResourceType</ows:Value>
-        <ows:Value>Degree</ows:Value>
-        <ows:Value>Lineage</ows:Value>
-        <ows:Value>SpecificationTitle</ows:Value>
-      </ows:Constraint>
-    </ows:Operation>
-    <ows:Operation name="GetRecordById">
-      <ows:DCP>
-        <ows:HTTP>
-          <ows:Get xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-          <ows:Post xlink:href="http://localhost:8080/geonetwork/srv/en/csw">
-            <ows:Constraint name="PostEncoding">
-              <ows:Value>XML</ows:Value>
-              <ows:Value>SOAP</ows:Value>
-            </ows:Constraint>
-          </ows:Post>
-        </ows:HTTP>
-      </ows:DCP>
-      <ows:Parameter name="outputSchema">
-        <ows:Value>http://www.opengis.net/cat/csw/2.0.2</ows:Value>
-        <ows:Value>http://www.isotc211.org/2005/gmd</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="outputFormat">
-        <ows:Value>application/xml</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="resultType">
-        <ows:Value>hits</ows:Value>
-        <ows:Value>results</ows:Value>
-        <ows:Value>validate</ows:Value>
-      </ows:Parameter>
-      <ows:Parameter name="ElementSetName">
-        <ows:Value>brief</ows:Value>
-        <ows:Value>summary</ows:Value>
-        <ows:Value>full</ows:Value>
-      </ows:Parameter>
-      <ows:Constraint name="PostEncoding">
-        <ows:Value>XML</ows:Value>
-      </ows:Constraint>
-    </ows:Operation>
-    <ows:Operation name="Transaction">
-      <ows:DCP>
-        <ows:HTTP>
-          <ows:Get xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-          <ows:Post xlink:href="http://localhost:8080/geonetwork/srv/en/csw" />
-        </ows:HTTP>
-      </ows:DCP>
-    </ows:Operation>
-    <!--        
-        <ows:Operation name="Harvest">
-            <ows:DCP>
-                <ows:HTTP>
-                    <ows:Get  xlink:href="http://$HOST:$PORT$SERVLET/srv/en/csw" />
-                    <ows:Post xlink:href="http://$HOST:$PORT$SERVLET/srv/en/csw"  />
-                </ows:HTTP>
-            </ows:DCP>
-        </ows:Operation>
--->
-    <ows:Parameter name="service">
-      <ows:Value>http://www.opengis.net/cat/csw/2.0.2</ows:Value>
-    </ows:Parameter>
-    <ows:Parameter name="version">
-      <ows:Value>2.0.2</ows:Value>
-    </ows:Parameter>
-    <ows:Constraint name="IsoProfiles">
-      <ows:Value>http://www.isotc211.org/2005/gmd</ows:Value>
-    </ows:Constraint>
-    <ows:Constraint name="PostEncoding">
-      <ows:Value>SOAP</ows:Value>
-    </ows:Constraint>
-  </ows:OperationsMetadata>
-  <ogc:Filter_Capabilities>
-    <ogc:Spatial_Capabilities>
-      <ogc:GeometryOperands>
-        <ogc:GeometryOperand>gml:Envelope</ogc:GeometryOperand>
-        <ogc:GeometryOperand>gml:Point</ogc:GeometryOperand>
-        <ogc:GeometryOperand>gml:LineString</ogc:GeometryOperand>
-        <ogc:GeometryOperand>gml:Polygon</ogc:GeometryOperand>
-      </ogc:GeometryOperands>
-      <ogc:SpatialOperators>
-        <ogc:SpatialOperator name="BBOX" />
-        <ogc:SpatialOperator name="Equals" />
-        <ogc:SpatialOperator name="Overlaps" />
-        <ogc:SpatialOperator name="Disjoint" />
-        <ogc:SpatialOperator name="Intersects" />
-        <ogc:SpatialOperator name="Touches" />
-        <ogc:SpatialOperator name="Crosses" />
-        <ogc:SpatialOperator name="Within" />
-        <ogc:SpatialOperator name="Contains" />
-        <!--
-                <ogc:SpatialOperator name="Beyond"/>
-                <ogc:SpatialOperator name="DWithin"/>
-                 The 'SpatialOperator' element can have a GeometryOperands child -->
-      </ogc:SpatialOperators>
-    </ogc:Spatial_Capabilities>
-    <ogc:Scalar_Capabilities>
-      <ogc:LogicalOperators />
-      <ogc:ComparisonOperators>
-        <ogc:ComparisonOperator>EqualTo</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>Like</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>LessThan</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>GreaterThan</ogc:ComparisonOperator>
-        <!-- LessThanOrEqualTo is in OGC Filter Spec, LessThanEqualTo is in OGC CSW schema -->
-        <ogc:ComparisonOperator>LessThanEqualTo</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>LessThanOrEqualTo</ogc:ComparisonOperator>
-        <!-- GreaterThanOrEqualTo is in OGC Filter Spec, GreaterThanEqualTo is in OGC CSW schema -->
-        <ogc:ComparisonOperator>GreaterThanEqualTo</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>GreaterThanOrEqualTo</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>NotEqualTo</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>Between</ogc:ComparisonOperator>
-        <ogc:ComparisonOperator>NullCheck</ogc:ComparisonOperator>
-        <!-- FIXME : Check NullCheck operation is available -->
-      </ogc:ComparisonOperators>
-    </ogc:Scalar_Capabilities>
-    <ogc:Id_Capabilities>
-      <ogc:EID />
-      <ogc:FID />
-    </ogc:Id_Capabilities>
-  </ogc:Filter_Capabilities>
-</csw:Capabilities>""" 
-
-    get_records_request_xml = """<?xml version="1.0"?>
-<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
-    xmlns:gmd="http://www.isotc211.org/2005/gmd" service="CSW" version="2.0.2" resultType="results">
-    <csw:Query typeNames="gmd:MD_Metadata">
-        <csw:ElementName>dc:identifier</csw:ElementName>
-        <csw:Constraint version="1.1.0">
-            <Filter xmlns="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"/>
-        </csw:Constraint>
-    </csw:Query>
-</csw:GetRecords>"""
-
-    get_records_response_xml = """<?xml version="1.0" encoding="UTF-8"?>
-<csw:GetRecordsResponse xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
-  <csw:SearchStatus timestamp="2010-10-21T14:54:45" />
-  <csw:SearchResults numberOfRecordsMatched="3" numberOfRecordsReturned="3" elementSet="summary" nextRecord="0">
-    <csw:Record xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dct="http://purl.org/dc/terms/">
-      <dc:identifier>521ca63d-dad9-43fe-aebe-1138ffee530f</dc:identifier>
-    </csw:Record>
-    <csw:Record xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dct="http://purl.org/dc/terms/">
-      <dc:identifier>8dc2dddd-e483-4c1a-9482-eb05e8e4314d</dc:identifier>
-    </csw:Record>
-    <csw:Record xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dct="http://purl.org/dc/terms/">
-      <dc:identifier>8d2aaadd-6ad8-41e0-9cd3-ef743ba19887</dc:identifier>
-    </csw:Record>
-  </csw:SearchResults>
-</csw:GetRecordsResponse>"""
-
-    get_record_by_id_request_xml1 = """<?xml version="1.0"?>
-<csw:GetRecordById xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2"
-    outputSchema="csw:IsoRecord">
-    <csw:Id>8dc2dddd-e483-4c1a-9482-eb05e8e4314d</csw:Id>
-</csw:GetRecordById>"""
-
-    get_record_by_id_response_xml1 = """<?xml version="1.0" encoding="UTF-8"?>
-<csw:GetRecordByIdResponse xmlns:csw="http://www.opengis.net/cat/csw/2.0.2">
-  <gmd:MD_Metadata xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gsr="http://www.isotc211.org/2005/gsr" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:gss="http://www.isotc211.org/2005/gss" xmlns:gts="http://www.isotc211.org/2005/gts" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:gmx="http://www.isotc211.org/2005/gmx" xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:geonet="http://www.fao.org/geonetwork">
-    <gmd:fileIdentifier xmlns:gml="http://www.opengis.net/gml" xmlns:srv="http://www.isotc211.org/2005/srv">
-      <gco:CharacterString>8dc2dddd-e483-4c1a-9482-eb05e8e4314d</gco:CharacterString>
-    </gmd:fileIdentifier>
-    <gmd:language>
-      <gmd:LanguageCode codeList="http://www.loc.gov/standards/iso639-2/php/code_list.php" codeListValue="eng">eng</gmd:LanguageCode>
-    </gmd:language>
-    <gmd:hierarchyLevel>
-      <gmd:MD_ScopeCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#MD_ScopeCode" codeListValue="dataset" />
-    </gmd:hierarchyLevel>
-    <gmd:dateStamp>
-      <gco:DateTime xmlns:gml="http://www.opengis.net/gml" xmlns:srv="http://www.isotc211.org/2005/srv">2010-10-18T18:51:08</gco:DateTime>
-    </gmd:dateStamp>
-    <gmd:referenceSystemInfo>
-      <gmd:MD_ReferenceSystem>
-        <gmd:referenceSystemIdentifier>
-          <gmd:RS_Identifier>
-            <gmd:code>
-              <gco:CharacterString>urn:ogc:def:crs:EPSG::4258</gco:CharacterString>
-            </gmd:code>
-          </gmd:RS_Identifier>
-        </gmd:referenceSystemIdentifier>
-      </gmd:MD_ReferenceSystem>
-    </gmd:referenceSystemInfo>
-    <gmd:identificationInfo>
-      <gmd:MD_DataIdentification>
-        <gmd:citation>
-          <gmd:CI_Citation>
-            <gmd:title>
-              <gco:CharacterString>CKAN Dataset Example 1</gco:CharacterString>
-            </gmd:title>
-            <gmd:date>
-              <gmd:CI_Date>
-                <gmd:date>
-                  <gco:Date>2010-07-16</gco:Date>
-                </gmd:date>
-                <gmd:dateType>
-                  <gmd:CI_DateTypeCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_DateTypeCode" codeListValue="revision" />
-                </gmd:dateType>
-              </gmd:CI_Date>
-            </gmd:date>
-            <gmd:identifier>
-              <gmd:RS_Identifier>
-                <gmd:code>
-                  <gco:CharacterString>Please enter the unique identifier of the dataset</gco:CharacterString>
-                </gmd:code>
-                <gmd:codeSpace>
-                  <gco:CharacterString>Please enter the code space of the unique identifier of the dataset</gco:CharacterString>
-                </gmd:codeSpace>
-              </gmd:RS_Identifier>
-            </gmd:identifier>
-          </gmd:CI_Citation>
-        </gmd:citation>
-        <gmd:abstract>
-          <gco:CharacterString>This is just an example metadata record, created for the purpose of developing CKAN CSW client capabilities.</gco:CharacterString>
-        </gmd:abstract>
-        <gmd:pointOfContact>
-          <gmd:CI_ResponsibleParty>
-            <gmd:organisationName>
-              <gco:CharacterString>Please enter an organisation name for responsible organisation one</gco:CharacterString>
-            </gmd:organisationName>
-            <gmd:positionName>
-              <gco:CharacterString>Please enter a position name for responsible organisation one</gco:CharacterString>
-            </gmd:positionName>
-            <gmd:contactInfo>
-              <gmd:CI_Contact>
-                <gmd:address>
-                  <gmd:CI_Address>
-                    <gmd:electronicMailAddress>
-                      <gco:CharacterString>john.bywater at appropriatesoftware.net</gco:CharacterString>
-                    </gmd:electronicMailAddress>
-                  </gmd:CI_Address>
-                </gmd:address>
-              </gmd:CI_Contact>
-            </gmd:contactInfo>
-            <gmd:role>
-              <gmd:CI_RoleCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_RoleCode" codeListValue="distributor" />
-            </gmd:role>
-          </gmd:CI_ResponsibleParty>
-        </gmd:pointOfContact>
-        <gmd:pointOfContact>
-          <gmd:CI_ResponsibleParty>
-            <gmd:organisationName>
-              <gco:CharacterString>Please enter an organisation name for responsible organisation two</gco:CharacterString>
-            </gmd:organisationName>
-            <gmd:contactInfo>
-              <gmd:CI_Contact>
-                <gmd:address>
-                  <gmd:CI_Address>
-                    <gmd:electronicMailAddress>
-                      <gco:CharacterString>john.bywater at appropriatesoftware.net</gco:CharacterString>
-                    </gmd:electronicMailAddress>
-                  </gmd:CI_Address>
-                </gmd:address>
-              </gmd:CI_Contact>
-            </gmd:contactInfo>
-            <gmd:role>
-              <gmd:CI_RoleCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_RoleCode" codeListValue="pointOfContact" />
-            </gmd:role>
-          </gmd:CI_ResponsibleParty>
-        </gmd:pointOfContact>
-        <gmd:resourceConstraints>
-          <gmd:MD_LegalConstraints>
-            <gmd:accessConstraints>
-              <gmd:MD_RestrictionCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#MD_RestrictionCode" codeListValue="otherRestrictions" />
-            </gmd:accessConstraints>
-          </gmd:MD_LegalConstraints>
-        </gmd:resourceConstraints>
-        <gmd:resourceConstraints>
-          <gmd:MD_Constraints>
-            <gmd:useLimitation>
-              <gco:CharacterString>Use limitation</gco:CharacterString>
-            </gmd:useLimitation>
-          </gmd:MD_Constraints>
-        </gmd:resourceConstraints>
-        <gmd:spatialResolution>
-          <gmd:MD_Resolution>
-            <gmd:distance>
-              <gco:Distance uom="m">1</gco:Distance>
-            </gmd:distance>
-          </gmd:MD_Resolution>
-        </gmd:spatialResolution>
-        <gmd:spatialResolution>
-          <gmd:MD_Resolution>
-            <gmd:equivalentScale>
-              <gmd:MD_RepresentativeFraction>
-                <gmd:denominator>
-                  <gco:Integer>5000</gco:Integer>
-                </gmd:denominator>
-              </gmd:MD_RepresentativeFraction>
-            </gmd:equivalentScale>
-          </gmd:MD_Resolution>
-        </gmd:spatialResolution>
-        <gmd:language>
-          <gmd:LanguageCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#LanguageCode" codeListValue="eng" />
-        </gmd:language>
-        <gmd:topicCategory>
-          <gmd:MD_TopicCategoryCode>intelligenceMilitary</gmd:MD_TopicCategoryCode>
-        </gmd:topicCategory>
-        <gmd:extent>
-          <gmd:EX_Extent>
-            <gmd:geographicElement>
-              <gmd:EX_GeographicBoundingBox>
-                <gmd:westBoundLongitude>
-                  <gco:Decimal>-5.4457177734375</gco:Decimal>
-                </gmd:westBoundLongitude>
-                <gmd:southBoundLatitude>
-                  <gco:Decimal>50.115576171875</gco:Decimal>
-                </gmd:southBoundLatitude>
-                <gmd:eastBoundLongitude>
-                  <gco:Decimal>-5.0721826171875</gco:Decimal>
-                </gmd:eastBoundLongitude>
-                <gmd:northBoundLatitude>
-                  <gco:Decimal>50.428686523437</gco:Decimal>
-                </gmd:northBoundLatitude>
-              </gmd:EX_GeographicBoundingBox>
-            </gmd:geographicElement>
-          </gmd:EX_Extent>
-        </gmd:extent>
-      </gmd:MD_DataIdentification>
-    </gmd:identificationInfo>
-    <gmd:distributionInfo>
-      <gmd:MD_Distribution>
-        <gmd:distributionFormat>
-          <gmd:MD_Format>
-            <gmd:name>
-              <gco:CharacterString>Please enter the name of the format of the service</gco:CharacterString>
-            </gmd:name>
-            <gmd:version>
-              <gco:CharacterString>Please enter the version of the format of the service</gco:CharacterString>
-            </gmd:version>
-          </gmd:MD_Format>
-        </gmd:distributionFormat>
-        <gmd:transferOptions>
-          <gmd:MD_DigitalTransferOptions>
-            <gmd:onLine>
-              <gmd:CI_OnlineResource>
-                <gmd:linkage>
-                  <gmd:URL>http://appropriatesoftware.net/</gmd:URL>
-                </gmd:linkage>
-              </gmd:CI_OnlineResource>
-            </gmd:onLine>
-            <gmd:onLine>
-              <gmd:CI_OnlineResource>
-                <gmd:linkage>
-                  <gmd:URL>http://appropriatesoftware.net/</gmd:URL>
-                </gmd:linkage>
-              </gmd:CI_OnlineResource>
-            </gmd:onLine>
-          </gmd:MD_DigitalTransferOptions>
-        </gmd:transferOptions>
-      </gmd:MD_Distribution>
-    </gmd:distributionInfo>
-    <gmd:dataQualityInfo>
-      <gmd:DQ_DataQuality>
-        <gmd:lineage>
-          <gmd:LI_Lineage>
-            <gmd:statement>
-              <gco:CharacterString>Please enter a statement describing the events or sources used in the construction of this dataset</gco:CharacterString>
-            </gmd:statement>
-          </gmd:LI_Lineage>
-        </gmd:lineage>
-      </gmd:DQ_DataQuality>
-    </gmd:dataQualityInfo>
-  </gmd:MD_Metadata>
-</csw:GetRecordByIdResponse>"""
-
-    get_record_by_id_request_xml2 = """<?xml version="1.0"?>
-<csw:GetRecordById xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2"
-    outputSchema="csw:IsoRecord">
-    <csw:Id>521ca63d-dad9-43fe-aebe-1138ffee530f</csw:Id>
-</csw:GetRecordById>"""
-
-    get_record_by_id_response_xml2 = """<?xml version="1.0" encoding="UTF-8"?>
-<csw:GetRecordByIdResponse xmlns:csw="http://www.opengis.net/cat/csw/2.0.2">
-  <gmd:MD_Metadata xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gts="http://www.isotc211.org/2005/gts" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:gmx="http://www.isotc211.org/2005/gmx" xmlns:gsr="http://www.isotc211.org/2005/gsr" xmlns:gss="http://www.isotc211.org/2005/gss" xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:geonet="http://www.fao.org/geonetwork">
-    <gmd:fileIdentifier xmlns:gml="http://www.opengis.net/gml" xmlns:srv="http://www.isotc211.org/2005/srv">
-      <gco:CharacterString>521ca63d-dad9-43fe-aebe-1138ffee530f</gco:CharacterString>
-    </gmd:fileIdentifier>
-    <gmd:language>
-      <gmd:LanguageCode codeList="http://www.loc.gov/standards/iso639-2/php/code_list.php" codeListValue="eng">eng</gmd:LanguageCode>
-    </gmd:language>
-    <gmd:hierarchyLevel>
-      <gmd:MD_ScopeCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#MD_ScopeCode" codeListValue="dataset" />
-    </gmd:hierarchyLevel>
-    <gmd:dateStamp>
-      <gco:DateTime xmlns:gml="http://www.opengis.net/gml" xmlns:srv="http://www.isotc211.org/2005/srv">2010-10-12T16:02:48</gco:DateTime>
-    </gmd:dateStamp>
-    <gmd:referenceSystemInfo>
-      <gmd:MD_ReferenceSystem>
-        <gmd:referenceSystemIdentifier>
-          <gmd:RS_Identifier>
-            <gmd:code>
-              <gco:CharacterString>urn:ogc:def:crs:EPSG::4258</gco:CharacterString>
-            </gmd:code>
-          </gmd:RS_Identifier>
-        </gmd:referenceSystemIdentifier>
-      </gmd:MD_ReferenceSystem>
-    </gmd:referenceSystemInfo>
-    <gmd:identificationInfo>
-      <gmd:MD_DataIdentification>
-        <gmd:citation>
-          <gmd:CI_Citation>
-            <gmd:title>
-              <gco:CharacterString>Justin's Initial Demo Record.</gco:CharacterString>
-            </gmd:title>
-            <gmd:date>
-              <gmd:CI_Date>
-                <gmd:date>
-                  <gco:Date>2010-07-16</gco:Date>
-                </gmd:date>
-                <gmd:dateType>
-                  <gmd:CI_DateTypeCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_DateTypeCode" codeListValue="revision" />
-                </gmd:dateType>
-              </gmd:CI_Date>
-            </gmd:date>
-            <gmd:identifier>
-              <gmd:RS_Identifier>
-                <gmd:code>
-                  <gco:CharacterString>Please enter the unique identifier of the dataset</gco:CharacterString>
-                </gmd:code>
-                <gmd:codeSpace>
-                  <gco:CharacterString>Please enter the code space of the unique identifier of the dataset</gco:CharacterString>
-                </gmd:codeSpace>
-              </gmd:RS_Identifier>
-            </gmd:identifier>
-          </gmd:CI_Citation>
-        </gmd:citation>
-        <gmd:abstract>
-          <gco:CharacterString>Please enter an abstract, describing the data set more fully</gco:CharacterString>
-        </gmd:abstract>
-        <gmd:pointOfContact>
-          <gmd:CI_ResponsibleParty>
-            <gmd:organisationName>
-              <gco:CharacterString>Please enter an organisation name for responsible organisation one</gco:CharacterString>
-            </gmd:organisationName>
-            <gmd:positionName>
-              <gco:CharacterString>Please enter a position name for responsible organisation one</gco:CharacterString>
-            </gmd:positionName>
-            <gmd:contactInfo>
-              <gmd:CI_Contact>
-                <gmd:address>
-                  <gmd:CI_Address>
-                    <gmd:electronicMailAddress>
-                      <gco:CharacterString>Please enter an email address for responsible organisation one</gco:CharacterString>
-                    </gmd:electronicMailAddress>
-                  </gmd:CI_Address>
-                </gmd:address>
-              </gmd:CI_Contact>
-            </gmd:contactInfo>
-            <gmd:role>
-              <gmd:CI_RoleCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_RoleCode" codeListValue="distributor" />
-            </gmd:role>
-          </gmd:CI_ResponsibleParty>
-        </gmd:pointOfContact>
-        <gmd:pointOfContact>
-          <gmd:CI_ResponsibleParty>
-            <gmd:organisationName>
-              <gco:CharacterString>Please enter an organisation name for responsible organisation two</gco:CharacterString>
-            </gmd:organisationName>
-            <gmd:contactInfo>
-              <gmd:CI_Contact>
-                <gmd:address>
-                  <gmd:CI_Address>
-                    <gmd:electronicMailAddress>
-                      <gco:CharacterString>Please enter an email address for responsible organisation two</gco:CharacterString>
-                    </gmd:electronicMailAddress>
-                  </gmd:CI_Address>
-                </gmd:address>
-              </gmd:CI_Contact>
-            </gmd:contactInfo>
-            <gmd:role>
-              <gmd:CI_RoleCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_RoleCode" codeListValue="pointOfContact" />
-            </gmd:role>
-          </gmd:CI_ResponsibleParty>
-        </gmd:pointOfContact>
-        <gmd:resourceConstraints>
-          <gmd:MD_LegalConstraints>
-            <gmd:accessConstraints>
-              <gmd:MD_RestrictionCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#MD_RestrictionCode" codeListValue="otherRestrictions" />
-            </gmd:accessConstraints>
-          </gmd:MD_LegalConstraints>
-        </gmd:resourceConstraints>
-        <gmd:resourceConstraints>
-          <gmd:MD_Constraints>
-            <gmd:useLimitation>
-              <gco:CharacterString>Use limitation</gco:CharacterString>
-            </gmd:useLimitation>
-          </gmd:MD_Constraints>
-        </gmd:resourceConstraints>
-        <gmd:spatialResolution>
-          <gmd:MD_Resolution>
-            <gmd:distance>
-              <gco:Distance uom="urn:ogc:def:uom:EPSG::9001">Please enter the spatial resolution in metres</gco:Distance>
-            </gmd:distance>
-          </gmd:MD_Resolution>
-        </gmd:spatialResolution>
-        <gmd:spatialResolution>
-          <gmd:MD_Resolution>
-            <gmd:equivalentScale>
-              <gmd:MD_RepresentativeFraction>
-                <gmd:denominator>
-                  <gco:Integer>Please enter the denominator of the equivalent scale</gco:Integer>
-                </gmd:denominator>
-              </gmd:MD_RepresentativeFraction>
-            </gmd:equivalentScale>
-          </gmd:MD_Resolution>
-        </gmd:spatialResolution>
-        <gmd:language>
-          <gmd:LanguageCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#LanguageCode" codeListValue="eng" />
-        </gmd:language>
-        <gmd:topicCategory>
-          <gmd:MD_TopicCategoryCode />
-        </gmd:topicCategory>
-        <gmd:extent>
-          <gmd:EX_Extent>
-            <gmd:geographicElement>
-              <gmd:EX_GeographicBoundingBox>
-                <gmd:westBoundLongitude>
-                  <gco:Decimal>-8.17</gco:Decimal>
-                </gmd:westBoundLongitude>
-                <gmd:southBoundLatitude>
-                  <gco:Decimal>49.96</gco:Decimal>
-                </gmd:southBoundLatitude>
-                <gmd:eastBoundLongitude>
-                  <gco:Decimal>1.75</gco:Decimal>
-                </gmd:eastBoundLongitude>
-                <gmd:northBoundLatitude>
-                  <gco:Decimal>60.84</gco:Decimal>
-                </gmd:northBoundLatitude>
-              </gmd:EX_GeographicBoundingBox>
-            </gmd:geographicElement>
-          </gmd:EX_Extent>
-        </gmd:extent>
-      </gmd:MD_DataIdentification>
-    </gmd:identificationInfo>
-    <gmd:distributionInfo>
-      <gmd:MD_Distribution>
-        <gmd:distributionFormat>
-          <gmd:MD_Format>
-            <gmd:name>
-              <gco:CharacterString>Please enter the name of the format of the service</gco:CharacterString>
-            </gmd:name>
-            <gmd:version>
-              <gco:CharacterString>Please enter the version of the format of the service</gco:CharacterString>
-            </gmd:version>
-          </gmd:MD_Format>
-        </gmd:distributionFormat>
-        <gmd:transferOptions>
-          <gmd:MD_DigitalTransferOptions>
-            <gmd:onLine>
-              <gmd:CI_OnlineResource>
-                <gmd:linkage>
-                  <gmd:URL>Please enter the url for further information about the dataset</gmd:URL>
-                </gmd:linkage>
-              </gmd:CI_OnlineResource>
-            </gmd:onLine>
-            <gmd:onLine>
-              <gmd:CI_OnlineResource>
-                <gmd:linkage>
-                  <gmd:URL>Please enter the url for the dataset</gmd:URL>
-                </gmd:linkage>
-              </gmd:CI_OnlineResource>
-            </gmd:onLine>
-          </gmd:MD_DigitalTransferOptions>
-        </gmd:transferOptions>
-      </gmd:MD_Distribution>
-    </gmd:distributionInfo>
-    <gmd:dataQualityInfo>
-      <gmd:DQ_DataQuality>
-        <gmd:lineage>
-          <gmd:LI_Lineage>
-            <gmd:statement>
-              <gco:CharacterString>Please enter a statement describing the events or sources used in the construction of this dataset</gco:CharacterString>
-            </gmd:statement>
-          </gmd:LI_Lineage>
-        </gmd:lineage>
-      </gmd:DQ_DataQuality>
-    </gmd:dataQualityInfo>
-  </gmd:MD_Metadata>
-</csw:GetRecordByIdResponse>"""
-
-    get_record_by_id_request_xml3 = """<?xml version="1.0"?>
-<csw:GetRecordById xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2"
-    outputSchema="csw:IsoRecord">
-    <csw:Id>8d2aaadd-6ad8-41e0-9cd3-ef743ba19887</csw:Id>
-</csw:GetRecordById>"""
-
-    get_record_by_id_response_xml3 = """<?xml version="1.0" encoding="UTF-8"?>
-<csw:GetRecordByIdResponse xmlns:csw="http://www.opengis.net/cat/csw/2.0.2">
-  <gmd:MD_Metadata xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gts="http://www.isotc211.org/2005/gts" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:gmx="http://www.isotc211.org/2005/gmx" xmlns:gsr="http://www.isotc211.org/2005/gsr" xmlns:gss="http://www.isotc211.org/2005/gss" xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:geonet="http://www.fao.org/geonetwork">
-    <gmd:fileIdentifier xmlns:gml="http://www.opengis.net/gml" xmlns:srv="http://www.isotc211.org/2005/srv">
-      <gco:CharacterString>8d2aaadd-6ad8-41e0-9cd3-ef743ba19887</gco:CharacterString>
-    </gmd:fileIdentifier>
-    <gmd:language>
-      <gmd:LanguageCode codeList="http://www.loc.gov/standards/iso639-2/php/code_list.php" codeListValue="eng">eng</gmd:LanguageCode>
-    </gmd:language>
-    <gmd:hierarchyLevel>
-      <gmd:MD_ScopeCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#MD_ScopeCode" codeListValue="dataset" />
-    </gmd:hierarchyLevel>
-    <gmd:dateStamp>
-      <gco:DateTime xmlns:gml="http://www.opengis.net/gml" xmlns:srv="http://www.isotc211.org/2005/srv">2010-10-21T11:44:10</gco:DateTime>
-    </gmd:dateStamp>
-    <gmd:referenceSystemInfo>
-      <gmd:MD_ReferenceSystem>
-        <gmd:referenceSystemIdentifier>
-          <gmd:RS_Identifier>
-            <gmd:code>
-              <gco:CharacterString>urn:ogc:def:crs:EPSG::4258</gco:CharacterString>
-            </gmd:code>
-          </gmd:RS_Identifier>
-        </gmd:referenceSystemIdentifier>
-      </gmd:MD_ReferenceSystem>
-    </gmd:referenceSystemInfo>
-    <gmd:identificationInfo>
-      <gmd:MD_DataIdentification>
-        <gmd:citation>
-          <gmd:CI_Citation>
-            <gmd:title>
-              <gco:CharacterString>Testing bug 62 - temporal extents</gco:CharacterString>
-            </gmd:title>
-            <gmd:date>
-              <gmd:CI_Date>
-                <gmd:date>
-                  <gco:Date>2010-07-16</gco:Date>
-                </gmd:date>
-                <gmd:dateType>
-                  <gmd:CI_DateTypeCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_DateTypeCode" codeListValue="revision" />
-                </gmd:dateType>
-              </gmd:CI_Date>
-            </gmd:date>
-            <gmd:identifier>
-              <gmd:RS_Identifier>
-                <gmd:code>
-                  <gco:CharacterString>Please enter the unique identifier of the dataset</gco:CharacterString>
-                </gmd:code>
-                <gmd:codeSpace>
-                  <gco:CharacterString>Please enter the code space of the unique identifier of the dataset</gco:CharacterString>
-                </gmd:codeSpace>
-              </gmd:RS_Identifier>
-            </gmd:identifier>
-          </gmd:CI_Citation>
-        </gmd:citation>
-        <gmd:abstract>
-          <gco:CharacterString>Please enter an abstract, describing the data set more fully</gco:CharacterString>
-        </gmd:abstract>
-        <gmd:pointOfContact>
-          <gmd:CI_ResponsibleParty>
-            <gmd:organisationName>
-              <gco:CharacterString>Please enter an organisation name for responsible organisation one</gco:CharacterString>
-            </gmd:organisationName>
-            <gmd:positionName>
-              <gco:CharacterString>Please enter a position name for responsible organisation one</gco:CharacterString>
-            </gmd:positionName>
-            <gmd:contactInfo>
-              <gmd:CI_Contact>
-                <gmd:address>
-                  <gmd:CI_Address>
-                    <gmd:electronicMailAddress>
-                      <gco:CharacterString>Please enter an email address for responsible organisation one</gco:CharacterString>
-                    </gmd:electronicMailAddress>
-                  </gmd:CI_Address>
-                </gmd:address>
-              </gmd:CI_Contact>
-            </gmd:contactInfo>
-            <gmd:role>
-              <gmd:CI_RoleCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_RoleCode" codeListValue="distributor" />
-            </gmd:role>
-          </gmd:CI_ResponsibleParty>
-        </gmd:pointOfContact>
-        <gmd:pointOfContact>
-          <gmd:CI_ResponsibleParty>
-            <gmd:organisationName>
-              <gco:CharacterString>Please enter an organisation name for responsible organisation two</gco:CharacterString>
-            </gmd:organisationName>
-            <gmd:contactInfo>
-              <gmd:CI_Contact>
-                <gmd:address>
-                  <gmd:CI_Address>
-                    <gmd:electronicMailAddress>
-                      <gco:CharacterString>Please enter an email address for responsible organisation two</gco:CharacterString>
-                    </gmd:electronicMailAddress>
-                  </gmd:CI_Address>
-                </gmd:address>
-              </gmd:CI_Contact>
-            </gmd:contactInfo>
-            <gmd:role>
-              <gmd:CI_RoleCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#CI_RoleCode" codeListValue="pointOfContact" />
-            </gmd:role>
-          </gmd:CI_ResponsibleParty>
-        </gmd:pointOfContact>
-        <gmd:resourceConstraints>
-          <gmd:MD_LegalConstraints>
-            <gmd:accessConstraints>
-              <gmd:MD_RestrictionCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#MD_RestrictionCode" codeListValue="otherRestrictions" />
-            </gmd:accessConstraints>
-          </gmd:MD_LegalConstraints>
-        </gmd:resourceConstraints>
-        <gmd:resourceConstraints>
-          <gmd:MD_Constraints>
-            <gmd:useLimitation>
-              <gco:CharacterString>Use limitation</gco:CharacterString>
-            </gmd:useLimitation>
-          </gmd:MD_Constraints>
-        </gmd:resourceConstraints>
-        <gmd:spatialResolution>
-          <gmd:MD_Resolution>
-            <gmd:distance>
-              <gco:Distance uom="urn:ogc:def:uom:EPSG::9001">Please enter the spatial resolution in metres</gco:Distance>
-            </gmd:distance>
-          </gmd:MD_Resolution>
-        </gmd:spatialResolution>
-        <gmd:spatialResolution>
-          <gmd:MD_Resolution>
-            <gmd:equivalentScale>
-              <gmd:MD_RepresentativeFraction>
-                <gmd:denominator>
-                  <gco:Integer>Please enter the denominator of the equivalent scale</gco:Integer>
-                </gmd:denominator>
-              </gmd:MD_RepresentativeFraction>
-            </gmd:equivalentScale>
-          </gmd:MD_Resolution>
-        </gmd:spatialResolution>
-        <gmd:language>
-          <gmd:LanguageCode codeList="http://www.isotc211.org/2005/resources/codeList.xml#LanguageCode" codeListValue="eng" />
-        </gmd:language>
-        <gmd:topicCategory>
-          <gmd:MD_TopicCategoryCode />
-        </gmd:topicCategory>
-        <gmd:extent>
-          <gmd:EX_Extent>
-            <gmd:geographicElement>
-              <gmd:EX_GeographicBoundingBox>
-                <gmd:westBoundLongitude>
-                  <gco:Decimal>-8.17</gco:Decimal>
-                </gmd:westBoundLongitude>
-                <gmd:southBoundLatitude>
-                  <gco:Decimal>49.96</gco:Decimal>
-                </gmd:southBoundLatitude>
-                <gmd:eastBoundLongitude>
-                  <gco:Decimal>1.75</gco:Decimal>
-                </gmd:eastBoundLongitude>
-                <gmd:northBoundLatitude>
-                  <gco:Decimal>60.84</gco:Decimal>
-                </gmd:northBoundLatitude>
-              </gmd:EX_GeographicBoundingBox>
-            </gmd:geographicElement>
-          </gmd:EX_Extent>
-        </gmd:extent>
-      </gmd:MD_DataIdentification>
-    </gmd:identificationInfo>
-    <gmd:distributionInfo>
-      <gmd:MD_Distribution>
-        <gmd:distributionFormat>
-          <gmd:MD_Format>
-            <gmd:name>
-              <gco:CharacterString>Please enter the name of the format of the service</gco:CharacterString>
-            </gmd:name>
-            <gmd:version>
-              <gco:CharacterString>Please enter the version of the format of the service</gco:CharacterString>
-            </gmd:version>
-          </gmd:MD_Format>
-        </gmd:distributionFormat>
-        <gmd:transferOptions>
-          <gmd:MD_DigitalTransferOptions>
-            <gmd:onLine>
-              <gmd:CI_OnlineResource>
-                <gmd:linkage>
-                  <gmd:URL>Please enter the url for further information about the dataset</gmd:URL>
-                </gmd:linkage>
-              </gmd:CI_OnlineResource>
-            </gmd:onLine>
-            <gmd:onLine>
-              <gmd:CI_OnlineResource>
-                <gmd:linkage>
-                  <gmd:URL>Please enter the url for the dataset</gmd:URL>
-                </gmd:linkage>
-              </gmd:CI_OnlineResource>
-            </gmd:onLine>
-          </gmd:MD_DigitalTransferOptions>
-        </gmd:transferOptions>
-      </gmd:MD_Distribution>
-    </gmd:distributionInfo>
-    <gmd:dataQualityInfo>
-      <gmd:DQ_DataQuality>
-        <gmd:lineage>
-          <gmd:LI_Lineage>
-            <gmd:statement>
-              <gco:CharacterString>Please enter a statement describing the events or sources used in the construction of this dataset</gco:CharacterString>
-            </gmd:statement>
-          </gmd:LI_Lineage>
-        </gmd:lineage>
-      </gmd:DQ_DataQuality>
-    </gmd:dataQualityInfo>
-  </gmd:MD_Metadata>
-</csw:GetRecordByIdResponse>"""
-
-    map = {
-        get_capabilities_request_xml: get_capabilities_response_xml,
-        get_records_request_xml: get_records_response_xml,
-        get_record_by_id_request_xml1: get_record_by_id_response_xml1,
-        get_record_by_id_request_xml2: get_record_by_id_response_xml2,
-        get_record_by_id_request_xml3: get_record_by_id_response_xml3,
-    }
-
-    def check_capabilities(self): pass
-
-    def login(self): pass
-
-    def logout(self): pass
-
-    def send(self, csw_request):
-        csw_request_xml = self.get_xml_from_csw_request(csw_request)
-        if csw_request_xml in self.map:
-            csw_response_xml = self.map[csw_request_xml]
-        else:
-            msg = "Mock send() method can't handle request: %s" % csw_request_xml
-            raise Exception, msg
-        return csw_response_xml
-
-


--- a/ckan/tests/lib/test_cswclient.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +0,0 @@
-from ckan.tests import CheckMethods
-from ckan.tests import SkipTest
-from pylons import config
-
-from ckan.lib.cswclient import CswError
-from ckan.lib.cswclient import CswGetCapabilities
-from ckan.lib.cswclient import CswGetRecords
-from ckan.lib.cswclient import CswGetRecordById
-from ckan.lib.cswclient import CswClient
-from ckan.lib.cswclient import GeoNetworkClient
-from mock_cswclient import MockGeoNetworkClient
-
-import socket
-socket.setdefaulttimeout(1)
-
-class CswRequestTestCase(CheckMethods):
-
-    request_class = None
-    request_params = {}
-    expect_xml = ""
-
-    def setup(self):
-        self.request = self.make_request()
-
-    def teardown(self):
-        self.request = None
-
-    def make_request(self):
-        if not self.request_class:
-            msg = "Test case '%s' has no request_class." % self.__class__
-            raise Exception, msg
-        return self.request_class(**self.request_params)
-
-    def test_msg(self):
-        request_xml = self.request.get_xml()
-        self.assert_equal(request_xml, self.expect_xml)
-
-
-class TestCswGetCapabilities(CswRequestTestCase):
-
-    request_class = CswGetCapabilities
-    request_params = {}
-    expect_xml = """<?xml version="1.0"?>
-<csw:GetCapabilities xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW">
-    <ows:AcceptVersions xmlns:ows="http://www.opengis.net/ows">
-        <ows:Version>2.0.2</ows:Version>
-    </ows:AcceptVersions>
-    <ows:AcceptFormats xmlns:ows="http://www.opengis.net/ows">
-        <ows:OutputFormat>application/xml</ows:OutputFormat>
-    </ows:AcceptFormats>
-</csw:GetCapabilities>"""
-
-
-class TestCswGetRecords(CswRequestTestCase):
-
-    request_class = CswGetRecords
-    request_params = {"result_type": "results"}
-    expect_xml = """<?xml version="1.0"?>
-<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
-    xmlns:gmd="http://www.isotc211.org/2005/gmd" service="CSW" version="2.0.2" resultType="results">
-    <csw:Query typeNames="gmd:MD_Metadata">
-        <csw:ElementName>dc:identifier</csw:ElementName>
-        <csw:Constraint version="1.1.0">
-            <Filter xmlns="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"/>
-        </csw:Constraint>
-    </csw:Query>
-</csw:GetRecords>"""
-
-
-class TestCswGetRecordById(CswRequestTestCase):
-
-    request_class = CswGetRecordById
-    request_params = {"identifier": "000000000000000000000000000000000000000"}
-    expect_xml = """<?xml version="1.0"?>
-<csw:GetRecordById xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" service="CSW" version="2.0.2"
-    outputSchema="csw:IsoRecord">
-    <csw:Id>000000000000000000000000000000000000000</csw:Id>
-</csw:GetRecordById>"""
-
-
-class CswClientTestCase(CheckMethods):
-
-    csw_client_class = CswClient
-    base_url = ""
-    username = ""
-    password = ""
-    max_records = 10
-    expected_id = '8dc2dddd-e483-4c1a-9482-eb05e8e4314d'
-
-    def setup(self):
-        self.client = None
-        self.client = self.create_csw_client()
-        if self.username and self.password:
-            self.client.login()
-
-    def teardown(self):
-        if self.username and self.password:
-            if hasattr(self, 'client') and self.client:
-                self.client.logout()
-        self.client = None
-
-    def create_csw_client(self):
-        return self.csw_client_class(
-            base_url=self.base_url,
-            username=self.username,
-            password=self.password,
-        )
-
-    def test_send_get_capabilities(self):
-        xml = self.client.send_get_capabilities()
-        self.assert_contains(xml, "csw:Capabilities")
-
-    def test_assert_capabilities(self):
-        self.client.assert_capabilities()
-
-    def test_send_get_records(self):
-        xml = self.client.send_get_records()
-        self.assert_contains(xml, "GetRecordsResponse")
-
-    def test_send_get_record_by_id(self):
-        xml = self.client.send_get_record_by_id("8dc2dddd-e483-4c1a-9482-eb05e8e4314d")
-        self.assert_contains(xml, "GetRecordByIdResponse")
-
-    def test_get_record_by_id(self):
-        xml = self.client.get_record_by_id("8dc2dddd-e483-4c1a-9482-eb05e8e4314d")
-        self.assert_contains(xml, "gmd:MD_Metadata")
-
-    def test_get_identifiers(self):
-        ids = self.client.get_identifiers()
-        self.assert_contains(ids, self.expected_id)
-
-    def test_get_records(self):
-        records = self.client.get_records(max_records=self.max_records)
-        self.assert_true(len(records))
-
-    def test_extract_identifiers(self):
-        get_records_response = """<?xml version="1.0" encoding="UTF-8"?>
-<csw:GetRecordsResponse xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
-  <csw:SearchStatus timestamp="2010-10-21T23:38:53" />
-  <csw:SearchResults numberOfRecordsMatched="3" numberOfRecordsReturned="3" elementSet="full" nextRecord="0">
-    <csw:Record xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dct="http://purl.org/dc/terms/">
-      <dc:identifier>521ca63d-dad9-43fe-aebe-1138ffee530f</dc:identifier>
-    </csw:Record>
-    <csw:Record xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dct="http://purl.org/dc/terms/">
-      <dc:identifier>8dc2dddd-e483-4c1a-9482-eb05e8e4314d</dc:identifier>
-    </csw:Record>
-    <csw:Record xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dct="http://purl.org/dc/terms/">
-      <dc:identifier>8d2aaadd-6ad8-41e0-9cd3-ef743ba19887</dc:identifier>
-    </csw:Record>
-  </csw:SearchResults>
-</csw:GetRecordsResponse>"""
-        ids = self.client.extract_identifiers(get_records_response)
-        self.assert_isinstance(ids, list)
-        self.assert_len(ids, 3)
-        self.assert_contains(ids, "521ca63d-dad9-43fe-aebe-1138ffee530f")
-        self.assert_contains(ids, "8dc2dddd-e483-4c1a-9482-eb05e8e4314d")
-        self.assert_contains(ids, "8d2aaadd-6ad8-41e0-9cd3-ef743ba19887")
-
-
-class BlankTests(CswClientTestCase):
-
-    def test_send_get_capabilities(self): pass
-
-    def test_assert_capabilities(self): pass
-
-    def test_send_get_records(self): pass
-
-    def test_send_get_record_by_id(self): pass
-
-    def test_get_record_by_id(self): pass
-
-    def test_get_identifiers(self): pass
-
-    def test_get_records(self): pass
-
-    def test_extract_identifiers(self): pass
-
-
-
-class GeoNetworkClientTestCase(CswClientTestCase):
-
-    csw_client_class = GeoNetworkClient
-
-
-class TestGeoNetworkClient(GeoNetworkClientTestCase):
-
-    csw_client_class = MockGeoNetworkClient
-
-
-class TestGeoNetworkClientSite(GeoNetworkClientTestCase):
-
-    # Test with real example GeoNetwork site.
-    base_url=config.get('example_csw_url', '')
-    username=config.get('example_csw_username', '')
-    password=config.get('example_csw_password', '')
-
-    def setup(self):
-        if not self.base_url:
-            raise SkipTest
-        super(TestGeoNetworkClientSite, self).setup()
-
-
-class TestGeoNetworkClientSiteNoAuth(TestGeoNetworkClientSite):
-
-    # Test with real example GeoNetwork site.
-    username = ''
-    password = ''
-    expected_id = '8d2aaadd-6ad8-41e0-9cd3-ef743ba19887'
-
-
-class TestGeoNetworkClientSiteBadAuth(BlankTests, TestGeoNetworkClientSite):
-
-    username = 'mickey'
-    password = 'mouse'
-    expected_id = '8d2aaadd-6ad8-41e0-9cd3-ef743ba19887'
-
-    # Since there is a bad username and password, setup will fail.
-    def setup(self):
-        super_method = super(TestGeoNetworkClientSiteBadAuth, self).setup
-        self.assert_raises(CswError, super_method)
-
-
-class TestGeoNetworkClientSiteDown(BlankTests, GeoNetworkClientTestCase):
-
-    base_url = 'http://128.0.0.1:44444'
-    username = 'a'
-    password = 'b'
-
-    # Since there is a username and password, setup and teardown will fail.
-    def setup(self):
-        super_method = super(TestGeoNetworkClientSiteDown, self).setup
-        self.assert_raises(CswError, super_method)
-
-    def teardown(self):
-        super_method = super(TestGeoNetworkClientSiteDown, self).teardown
-        self.assert_raises(CswError, super_method)
-
-
-class TestGeoNetworkClientSiteDownNoAuth(BlankTests, GeoNetworkClientTestCase):
-
-    base_url = 'http://128.0.0.1:44444'
-    username = ''
-    password = ''
-
-    # Since there is no username and password, setup and teardown won't error.
-
-    # However, the send methods won't work....
-    def test_send_get_record_by_id(self):
-        super_method = GeoNetworkClientTestCase.test_send_get_record_by_id
-        self.assert_raises(CswError, super_method, self)
-


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/tests/lib/test_navl.py	Mon Jul 18 09:57:42 2011 +0100
@@ -0,0 +1,397 @@
+from pprint import pprint, pformat
+from formencode import validators
+from nose.plugins.skip import SkipTest
+
+from ckan.lib.navl.dictization_functions import (
+    flatten_schema,
+    get_all_key_combinations,
+    make_full_schema,
+    flatten_dict,
+    unflatten,
+    missing,
+    augment_data,
+    validate,
+    validate_flattened)
+from ckan.lib.navl.validators import (
+    identity_converter,
+    empty,
+    not_empty,
+    ignore_missing,
+    default,
+    convert_int,
+    ignore)
+
+schema = {
+    "__after": [identity_converter],
+    "__extra": [identity_converter],
+    "__junk": [identity_converter],
+    "0": [identity_converter],
+    "1": [identity_converter],
+    "2": {
+        "__before": [identity_converter],
+        "__after": [identity_converter],
+        "20": [identity_converter],
+        "22": [identity_converter],
+        "21": {
+            "210": [identity_converter],
+        },
+    },
+    "3": {
+        "30": [identity_converter],
+    }
+}
+
+data = {
+    ("0",): "0 value",
+    #key 1 missing
+    ("2", 0, "20"): "20 value 0",
+    #key 2,22 missing
+        ("2", 0, "21", 0, "210"): "210 value 0,0",
+        #key 3 missing subdict
+        ("2", 1, "20"): "20 value 1",
+        ("2", 1, "22"): "22 value 1",
+        ("2", 1, "21", 0, "210"): "210 value 1,0",
+        ("2", 1, "21", 1, "210"): "210 value 1,1",
+        ("2", 1, "21", 3, "210"): "210 value 1,3", ##out of order sequence
+        ("4", 1, "30"): "30 value 1", #junk key as no 4 and no subdict
+        ("4",): "4 value", #extra key 4
+        ("2", 2, "21", 0, "210"): "210 value 2,0" #junk key as it does not have a parent
+    }
+
+class TestNavl:
+    def test_flatten_schema(self):
+
+        flattened_schema = flatten_schema(schema)
+
+        assert flattened_schema == {
+             ('0',): [identity_converter],
+             ('1',): [identity_converter],
+             ('2', '20'): [identity_converter],
+             ('2', '__after'): [identity_converter],
+             ('2', '__before'): [identity_converter],
+             ('2', '21', '210'): [identity_converter],
+             ('2', '22'): [identity_converter],
+             ('3', '30'): [identity_converter],
+             ('__after',): [identity_converter],
+             ('__extra',): [identity_converter],
+             ('__junk',): [identity_converter],
+        }, pprint(flattened_schema)
+
+    def test_get_key_combination(self):
+
+        flattened_schema = flatten_schema(schema)
+        assert get_all_key_combinations(data, flattened_schema) ==\
+            set([(),
+                ('2', 0), 
+                ('2', 1), 
+                ('2', 1, '21', 0),
+                ('2', 0, '21', 0),
+                ('2', 1, '21', 1), 
+                ('2', 1, '21', 3), 
+                ]), get_all_key_combinations(data, flattened_schema)
+
+        #state = {}
+        #make_flattened_schema(data, schema, state)
+
+    def test_make_full_schema(self):
+
+        full_schema = make_full_schema(data, schema)
+
+        print set(full_schema.keys()) - set(data.keys())
+
+        assert set(full_schema.keys()) - set(data.keys()) == set([('2', 1, '__before'),
+                                                                  ('2', 0, '__after'),
+                                                                  ('2', 0, '22'),
+                                                                  ('1',),
+                                                                  ('2', 1, '__after'),
+                                                                  ('2', 0, '__before'),
+                                                                  ('__after',),
+                                                                  ('__extra',),
+                                                                  ('__junk',),
+                                                                 ])
+
+        print set(data.keys()) - set(full_schema.keys())
+
+        assert set(data.keys()) - set(full_schema.keys()) == set([('2', 2, '21', 0, '210'),
+                                                                  ('4',),
+                                                                  ('4', 1, '30')])
+
+
+    def test_augment_junk_and_extras(self):
+        raise SkipTest()
+        assert augment_data(data, schema) == {
+             ('__junk',): {('2', 2, '21', 0, '210'): '210 value 2,0',
+                           ('4', 1, '30'): '30 value 1'},
+             ('0',): '0 value',
+             ('1',): missing,
+             ('2', 0, '20'): '20 value 0',
+             ('2', 0, '21', 0, '210'): '210 value 0,0',
+             ('2', 0, '22'): missing,
+             ('2', 1, '20'): '20 value 1',
+             ('2', 1, '21', 0, '210'): '210 value 1,0',
+             ('2', 1, '21', 1, '210'): '210 value 1,1',
+             ('2', 1, '21', 3, '210'): '210 value 1,3',
+             ('2', 1, '22'): '22 value 1',
+             ('__extras',): {'4': '4 value'}}, pprint(augment_data(data, schema))
+
+
+    def test_identity_validation(self):
+        raise SkipTest()
+
+        converted_data, errors = validate_flattened(data, schema)
+        print errors
+
+        assert not errors
+
+
+        assert sorted(converted_data) == sorted({
+             ('__junk',): {('2', 2, '21', 0, '210'): '210 value 2,0',
+                           ('4', 1, '30'): '30 value 1'},
+             ('0',): '0 value',
+             ('1',): missing,
+             ('2', 0, '20'): '20 value 0',
+             ('2', 0, '21', 0, '210'): '210 value 0,0',
+             ('2', 0, '22'): missing,
+             ('2', 1, '20'): '20 value 1',
+             ('2', 1, '21', 0, '210'): '210 value 1,0',
+             ('2', 1, '21', 1, '210'): '210 value 1,1',
+             ('2', 1, '21', 3, '210'): '210 value 1,3',
+             ('2', 1, '22'): '22 value 1',
+             ('__extras',): {'4': '4 value'}}), pprint(sorted(converted_data))
+
+
+    def test_basic_errors(self):
+        raise SkipTest()
+
+        schema = {
+            "__junk": [empty],
+            "__extras": [empty],
+            "0": [identity_converter],
+            "1": [not_empty],
+            "2": {
+                "__before": [identity_converter],
+                "__after": [identity_converter],
+                "20": [identity_converter],
+                "22": [identity_converter],
+                "__extras": [empty],
+                "21": {
+                    "210": [identity_converter],
+                },
+            },
+            "3": {
+                "30": [identity_converter],
+            },
+        }
+
+        converted_data, errors = validate_flattened(data, schema)
+
+        assert errors == {('__junk',): [u'The input field __junk was not expected.'], ('1',): [u'Missing value'], ('__extras',): [u'The input field __extras was not expected.']}, errors
+
+    def test_default(self):
+        schema = {
+            "__junk": [ignore],
+            "__extras": [ignore, default("weee")],
+            "__before": [ignore],
+            "__after": [ignore],
+            "0": [default("default")],
+            "1": [default("default")],
+        }
+
+        converted_data, errors = validate_flattened(data, schema)
+
+        assert not errors
+        assert converted_data == {('1',): 'default', ('0',): '0 value'}, converted_data
+
+
+    def test_ignore_missing(self):
+        schema = {
+            "__junk": [ignore],
+            "__extras": [ignore, default("weee")],
+            "0": [default("default")],
+            "1": [ignore_missing, default("default")],
+        }
+
+        converted_data, errors = validate_flattened(data, schema)
+
+        assert not errors
+        assert converted_data == {('0',): '0 value'}, converted_data
+
+
+    def test_flatten(self):
+
+        data = {'extras': [{'key': 'genre', 'value': u'horror'},
+                           {'key': 'media', 'value': u'dvd'}],
+                'license_id': u'gpl-3.0',
+                'name': u'testpkg',
+                'resources': [{u'alt_url': u'alt_url',
+                              u'description': u'Second file',
+                              u'extras': {u'size': u'200'},
+                              u'format': u'xml',
+                              u'hash': u'def123',
+                              u'url': u'http://blah.com/file2.xml'},
+                              {u'alt_url': u'alt_url',
+                              u'description': u'Main file',
+                              u'extras': {u'size': u'200'},
+                              u'format': u'xml',
+                              u'hash': u'abc123',
+                              u'url': u'http://blah.com/file.xml'}],
+                'tags': [{'name': u'russion'}, {'name': u'novel'}],
+                'title': u'Some Title',
+                'url': u'http://blahblahblah.mydomain'}
+
+        assert flatten_dict(data) == {('extras', 0, 'key'): 'genre',
+                                     ('extras', 0, 'value'): u'horror',
+                                     ('extras', 1, 'key'): 'media',
+                                     ('extras', 1, 'value'): u'dvd',
+                                     ('license_id',): u'gpl-3.0',
+                                     ('name',): u'testpkg',
+                                     ('resources', 0, u'alt_url'): u'alt_url',
+                                     ('resources', 0, u'description'): u'Second file',
+                                     ('resources', 0, u'extras'): {u'size': u'200'},
+                                     ('resources', 0, u'format'): u'xml',
+                                     ('resources', 0, u'hash'): u'def123',
+                                     ('resources', 0, u'url'): u'http://blah.com/file2.xml',
+                                     ('resources', 1, u'alt_url'): u'alt_url',
+                                     ('resources', 1, u'description'): u'Main file',
+                                     ('resources', 1, u'extras'): {u'size': u'200'},
+                                     ('resources', 1, u'format'): u'xml',
+                                     ('resources', 1, u'hash'): u'abc123',
+                                     ('resources', 1, u'url'): u'http://blah.com/file.xml',
+                                     ('tags', 0, 'name'): u'russion',
+                                     ('tags', 1, 'name'): u'novel',
+                                     ('title',): u'Some Title',
+                                     ('url',): u'http://blahblahblah.mydomain'}, pformat(flatten_dict(data))
+
+        assert data == unflatten(flatten_dict(data))
+
+
+    def test_simple(self):
+        raise SkipTest()
+        schema = {
+            "name": [not_empty],
+            "age": [ignore_missing, convert_int],
+            "gender": [default("female")],
+        }
+
+        data = {
+            "name": "fred",
+            "age": "32",
+        }
+
+
+        converted_data, errors = validate(data, schema)
+
+        assert not errors
+        assert converted_data == {'gender': 'female', 'age': 32, 'name': 'fred'}, converted_data
+
+        data = {
+            "name": "",
+            "age": "dsa32",
+            "extra": "extra",
+        }
+
+        converted_data, errors = validate(data, schema)
+
+        assert errors == {('age',): [u'Please enter an integer value'], ('name',): [u'Missing value']}, errors
+
+        assert converted_data == {'gender': 'female', 'age': 'dsa32', 'name': '', '__extras': {'extra': 'extra'}}
+
+
+        data = {"name": "fred",
+                "numbers": [{"number": "13221312"},
+                            {"number": "432423432", "code": "+44"}]
+                }
+
+        schema = {
+               "name": [not_empty],
+               "numbers": {
+                   "number": [convert_int],
+                   "code": [not_empty],
+                   "__extras": [ignore],
+               }
+            }
+
+        converted_data, errors = validate(data, schema)
+
+        assert errors == {('numbers', 0, 'code'): ['Missing value']}
+
+
+    def test_simple_converter_types(self):
+        schema = {
+            "name": [not_empty, unicode],
+            "age": [ignore_missing, int],
+            "gender": [default("female")],
+        }
+
+        data = {
+            "name": "fred",
+            "age": "32",
+        }
+
+        converted_data, errors = validate(data, schema)
+        assert not errors
+        assert converted_data == {'gender': 'female', 'age': 32, 'name': u'fred'}, converted_data
+
+        assert isinstance(converted_data["name"], unicode)
+        assert not isinstance(converted_data["gender"], unicode)
+
+
+    def test_formencode_compat(self):
+        raise SkipTest()
+        schema = {
+            "name": [not_empty, unicode],
+            "email": [validators.Email],
+            "email2": [validators.Email],
+        }
+
+        data = {
+            "name": "fred",
+            "email": "32",
+            "email2": "david at david.com",
+        }
+
+        converted_data, errors = validate(data, schema)
+        assert errors == {('email',): [u'An email address must contain a single @']}, errors
+
+    def test_range_validator(self):
+        raise SkipTest()
+
+        schema = {
+            "name": [not_empty, unicode],
+            "email": [validators.Int(min=1, max=10)],
+            "email2": [validators.Email],
+        }
+
+        data = {
+            "email": "32",
+            "email2": "david at david.com",
+        }
+
+        converted_data, errors = validate(data, schema)
+        assert errors == {('name',): [u'Missing value'], ('email',): [u'Please enter a number that is 10 or smaller']}, errors
+
+    def test_zz_formencode_translation(self):
+        raise SkipTest()
+
+        schema = {
+            "name": [not_empty, unicode],
+            "email": [validators.Email],
+            "email2": [validators.Email],
+        }
+
+        data = {
+            "email": "32",
+            "email2": "david at david.com",
+        }
+
+        import formencode
+        formencode.api.set_stdtranslation(languages = ['de'])
+        import ckan
+
+        converted_data, errors = validate(data, schema)
+        assert errors == {('name',): [u'Fehlender Wert'], ('email',): [u'Eine E-Mail-Adresse muss genau ein @-Zeichen enthalten']}, errors
+
+
+
+
+


--- a/ckan/tests/models/test_group.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/tests/models/test_group.py	Mon Jul 18 09:57:42 2011 +0100
@@ -40,7 +40,7 @@
         assert grp.title == u'Russian Group'
         anna = model.Package.by_name(u'annakarenina')
         war = model.Package.by_name(u'warandpeace')
-        assert grp.packages == [anna, war], grp.packages
+        assert set(grp.packages) == set((anna, war)), grp.packages
         assert grp in anna.groups
 
 


--- a/ckan/tests/pylons_controller.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/ckan/tests/pylons_controller.py	Mon Jul 18 09:57:42 2011 +0100
@@ -8,12 +8,18 @@
 from unittest import TestCase 
 from paste.registry import Registry 
 import pylons 
-from pylons.util import ContextObj 
+from pylons.util import AttribSafeContextObj
+import ckan.lib.app_globals as app_globals
 from pylons.controllers.util import Request, Response 
+from routes.util import URLGenerator
 
+from ckan.config.routing import make_map
 from ckan.tests import *
 from ckan.lib.cli import MockTranslator
 
+class TestSession(dict):
+    def save(self):
+        pass
 
 class PylonsTestCase(object):
     """A basic test case which allows access to pylons.c and pylons.request. 
@@ -23,9 +29,11 @@
         cls.registry=Registry() 
         cls.registry.prepare() 
 
-        cls.context_obj=ContextObj() 
+        cls.context_obj=AttribSafeContextObj()
         cls.registry.register(pylons.c, cls.context_obj)
-        pylons.c.errors = None
+
+        cls.app_globals_obj = app_globals.Globals()
+        cls.registry.register(pylons.g, cls.app_globals_obj)
 
         cls.request_obj=Request(dict(HTTP_HOST="nohost")) 
         cls.registry.register(pylons.request, cls.request_obj) 
@@ -37,4 +45,6 @@
         cls.registry.register(pylons.buffet, cls.buffet)
 
         cls.registry.register(pylons.response, Response())
-        cls.registry.register(pylons.url, None)
+        mapper = make_map()
+        cls.registry.register(pylons.url, URLGenerator(mapper, {}))
+        cls.registry.register(pylons.session, TestSession())


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/tests/test_mailer.py	Mon Jul 18 09:57:42 2011 +0100
@@ -0,0 +1,29 @@
+"""
+"""
+from pylons import config
+from ckan.lib.mailer import _mail_recipient
+from ckan.tests import *
+
+from smtpd import SMTPServer
+
+class TestMailer(TestController):
+
+    def setup(self):
+        config['smtp_server'] = 'localhost:667511'
+        config['ckan.mail_from'] = 'info at ckan.net'
+        class TestSMTPServer(SMTPServer):
+            def process_message(zelf, peer, mailfrom, rcpttos, data):
+                print "FOO"
+                return self.process_message(peer, mailfrom, rcpttos, data)
+        self.server = TestSMTPServer(('localhost', 6675), None)
+
+    def test_mail_recipient(self):
+    #    def tests(s, peer, mailfrom, rcpttos, data):
+    #        assert 'info at ckan.net' in mailfrom
+    #        assert 'foo at bar.com' in recpttos
+    #        assert 'i am a banana' in data
+    #    #self.process_message = tests
+    #    _mail_recipient('fooman', 'foo at localhost', 
+    #            'banaman', 'http://banana.com',
+    #            'i am a banana', 'this is a test')
+        pass


--- a/ckan/tests/test_rdf.py	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-from pylons import config
-import ckan.lib.helpers as h
-
-if 0: # DISABLED IMPORTS AND ALL TESTS
-    import ckan.lib.rdf as rdf
-    import ckan.lib.talis as talis
-    from ckan.tests import *
-    import ckan.model as model
-
-TALIS_STORE_NAME = 'ckan-dev1'
-
-class _TestRdf:
-    @classmethod
-    def setup_class(self):
-        CreateTestData.create_search_test_data()
-        self.rdf = rdf.RdfExporter()
-        self.talis = talis.Talis()
-        self.pkg_name = u'usa-courts-gov'
-
-    @classmethod
-    def teardown_class(self):
-        model.Session.remove()
-        model.repo.rebuild_db()
-
-    def test_1_package(self):
-        pkg = model.Package.by_name(self.pkg_name)
-        out = self.rdf.export_package(pkg)
-        assert pkg.name in out, out
-
-    def test_2_post(self):
-        pkg = model.Package.by_name(self.pkg_name)
-        rdf_xml = self.rdf.export_package(pkg)
-        service = '/meta'
-        err = self.talis.post(service, rdf_xml, with_password=True)
-        assert not err, err
-
-    def test_2_upload_package(self):
-        pkg = model.Package.by_name(self.pkg_name)
-        err = self.talis.post_pkg(pkg)
-        assert not err, err
-
-    def test_3_download_package(self):
-        res = self.talis.get_pkg(self.pkg_name)
-        assert not isinstance(res, Exception), res
-
-        pkg = model.Package.by_name(self.pkg_name)
-        assert pkg.name in res, res
-        assert pkg.title in res, res
-
-    def test_3_get(self):
-        uri = talis.TALIS_URI + TALIS_STORE_NAME + '/meta?about=%s&output=rdf' % (rdf.CKAN_SUBJECT_BASE + self.pkg_name)
-        res = self.talis.get(uri)
-        assert not isinstance(res, Exception), res
-
-        pkg = model.Package.by_name(self.pkg_name)
-        assert pkg.name in res, res
-        assert pkg.title in res, res
-
-        


--- a/doc/deployment.rst	Mon Jul 18 09:57:02 2011 +0100
+++ b/doc/deployment.rst	Mon Jul 18 09:57:42 2011 +0100
@@ -111,7 +111,7 @@
 
 6. Install code and dependent packages into the environment
 
-  Decide which release of CKAN you want to install. The CHANGELOG.txt has details on the releases. You'll need the exact tag name, and these are listed on the bitbucket page: https://bitbucket.org/okfn/ckan/src and hover over tags to see the options, e.g. ``ckan-1.4``.
+  Decide which release of CKAN you want to install. The CHANGELOG.txt has details on the releases. You'll need the exact tag name, and these are listed on the bitbucket page: https://bitbucket.org/okfn/ckan/src and hover over tags to see the options, e.g. ``ckan-1.4``. ::
 
   $ wget https://bitbucket.org/okfn/ckan/raw/ckan-1.4/pip-requirements.txt
 
@@ -239,7 +239,7 @@
 
 
 Upgrade
-=======
+-------
 
 Ideally production deployments are upgraded with fabric, but here are the manual instructions.
 
@@ -251,6 +251,12 @@
 2. It's probably wise to backup your database::
 
    $ paster --plugin=ckan db dump demo_ckan_backup.pg_dump --config=demo.ckan.net.ini
+ 
+   If you get a message about the command being 'mothballed' then you have a particularly old ckan! In this case, use pg_dump directly, specifying the database details from your config file.
+
+   $ grep -i sqlalchemy.url demo.ckan.net.ini 
+   sqlalchemy.url = postgres://okfn:testpassword@psql.okfn.org/demo.okfn.org
+   $ pg_dump -U okfn -h psql.okfn.org >demo_ckan_backup.pg_dump
 
 3. Get a version of pip-requirements.txt for the new version you want to install (see info on finding a suitable tag name above)::
 
@@ -268,7 +274,7 @@
 
    $ sudo /etc/init.d/apache2 restart
 
-7. You could manually try CKAN works in a browser, or better still run the smoke tests found in ckanext/blackbox. To do this, install ckanext and run ckanext from another machine - see ckanext README.txt for instructions: https://bitbucket.org/okfn/ckanext and then run::
+7. You could manually try CKAN in a browser, or better still run the smoke tests found in ckanext/blackbox. To do this, install ckanext and run ckanext from another machine - see ckanext README.txt for instructions: https://bitbucket.org/okfn/ckanext and then run::
 
    $ python blackbox/smoke.py blackbox/ckan.net.profile.json
 


--- a/doc/design.rst	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-===========
-CKAN Design
-===========
-
-Overview
-========
-
-As a system CKAN functions as a synthesis of several different services:
-
-.. image:: ckan-features.png
-  :alt: CKAN Features
-
-CKAN is part of a larger set of tools and services designed to enable automated
-use and reuse of content and data:
-
-.. image:: ckan-vision.png
-  :alt: The Debian of Data Services Stack
-
-Architecture
-============
-
-The key entity in CKAN is a Package. The data model for a package is pretty
-much what you see on: http://www.ckan.net/package/new
-
-This in turn is heavily based on the kind of packaging information provided for
-software but with some modifications. One of our aims is to keep things simple
-and as generic as possible as we have data from a lot of different domains.
-Thus we've tried to keep the core metadata pretty restricted but allow for
-additional info either via tags or via "extra" arbitrary key/value fields. So
-very roughly:
-
- * unique name
- * title
- * url + download url
- * author/maintainer info
- * license
- * notes
- * tags
- * [extendable with "extra" fields]
-
-All, the code is open-source and if you want to see the actual
-model definitions in code see here (probably start with core.py):
-
-<https://bitbucket.org/okfn/ckan/src/default/ckan/model/>
-
-One thing to note here is that all changes to package data are versioned in a
-wiki-like manner. This gives us a lot of flexibility in how we manage access to
-the system (as well as providing features like change logging for free!).
-
-The rdfizing code can be found here:
-
-<https://bitbucket.org/okfn/ckan/src/default/ckan/lib/rdf.py>
-


--- a/doc/index.rst	Mon Jul 18 09:57:02 2011 +0100
+++ b/doc/index.rst	Mon Jul 18 09:57:42 2011 +0100
@@ -11,7 +11,6 @@
    :maxdepth: 2
 
    README
-   design
    deployment
    configuration
    paster
@@ -26,7 +25,6 @@
    authentication
    authorization
    load_testing
-   distributed
    database_dumps
    deb
    vm


--- a/doc/kforge/index.html	Mon Jul 18 09:57:02 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-<html>
-  <head>
-    <title>CKAN - Comprehensive Knowledge Archive Network</title>
-    <link rel="stylesheet" href="http://m.okfn.org/okftext/css/okftext/text_basic.css" type="text/css" media="screen" charset="utf-8">
-    <meta http-equiv="REFRESH" content="0;url=/ckan/trac">
-  </head>
-  <body>
-    <h1>
-      CKAN
-    </h1>
-    <p>Redirecting to CKAN project homepage: <a href="http://knowledgeforge.net/ckan/trac/">http://knowledgeforge.net/ckan/trac/</a></p>
-  </body>
-</html>
-


--- a/fabfile.py	Mon Jul 18 09:57:02 2011 +0100
+++ b/fabfile.py	Mon Jul 18 09:57:42 2011 +0100
@@ -167,7 +167,7 @@
              db_user=None,
              db_pass='',
              db_host='localhost',
-             user=None
+             user='okfn'
         ):
     '''Configurable configuration: fab -d gives full info.
     


--- a/requires/lucid_present.txt	Mon Jul 18 09:57:02 2011 +0100
+++ b/requires/lucid_present.txt	Mon Jul 18 09:57:42 2011 +0100
@@ -10,6 +10,9 @@
 psycopg2==2.0.13
 lxml==2.2.4
 sphinx==0.6.4
+# Specifying not to use later webob because of incompatibility
+# with pylons 0.9.7 (change to imports of Multidict)
+webob<=1.0.8
 Pylons==0.9.7
 repoze.who==1.0.18
 tempita==0.4

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