[ckan-changes] commit/ckan: 3 new changesets
Bitbucket
commits-noreply at bitbucket.org
Mon Aug 8 17:23:51 UTC 2011
3 new changesets in ckan:
http://bitbucket.org/okfn/ckan/changeset/5e662137c341/
changeset: 5e662137c341
branch: feature-1253-authz-refactor
user: amercader
date: 2011-08-08 16:48:28
summary: [logic,authz] Fix auth for delete actions. Package ids are now stored in data_dict rather than context
affected #: 5 files (418 bytes)
--- a/ckan/controllers/api.py Fri Aug 05 18:17:13 2011 +0100
+++ b/ckan/controllers/api.py Mon Aug 08 15:48:28 2011 +0100
@@ -337,8 +337,10 @@
action_map[('package', type)] = delete.package_relationship_delete
context = {'model': model, 'session': model.Session, 'user': c.user,
- 'id': id, 'id2': id2, 'rel': subregister,
'api_version': ver}
+
+ data_dict = {'id': id, 'id2': id2, 'rel': subregister}
+
log.debug('delete %s/%s/%s/%s' % (register, id, subregister, id2))
action = action_map.get((register, subregister))
@@ -349,7 +351,7 @@
gettext('Cannot delete entity of this type: %s %s') %\
(register, subregister or ''))
try:
- response_data = action(context)
+ response_data = action(context, data_dict)
return self._finish_ok(response_data)
except NotAuthorized:
return self._finish_not_authz()
--- a/ckan/logic/__init__.py Fri Aug 05 18:17:13 2011 +0100
+++ b/ckan/logic/__init__.py Mon Aug 08 15:48:28 2011 +0100
@@ -92,10 +92,9 @@
model = context['model']
user = context.get('user')
- log.debug('check access - user %r' % user)
-
- if action and data_dict:
-
+ log.debug('check access - user %r, action %s' % (user,action))
+
+ if action:
#if action != model.Action.READ and user in (model.PSEUDO_USER__VISITOR, ''):
# # TODO Check the API key is valid at some point too!
# log.debug('Valid API key needed to make changes')
@@ -105,13 +104,12 @@
msg = logic_authorization.get('msg','')
raise NotAuthorized(msg)
#TODO: Is this really necessary?
- '''
elif not user:
msg = _('No valid API key provided.')
log.debug(msg)
raise NotAuthorized(msg)
#return AttributeDict(success=False, msg='No valid API key provided.')
- '''
+
log.debug('Access OK.')
return True
#return AttributeDict(success=True)
--- a/ckan/logic/action/delete.py Fri Aug 05 18:17:13 2011 +0100
+++ b/ckan/logic/action/delete.py Mon Aug 08 15:48:28 2011 +0100
@@ -6,18 +6,18 @@
from ckan.plugins import PluginImplementations, IGroupController, IPackageController
-def package_delete(context):
+def package_delete(context, data_dict):
model = context['model']
user = context['user']
- id = context["id"]
+ id = data_dict['id']
entity = model.Package.get(id)
if entity is None:
raise NotFound
- check_access_new('package_delete',context)
+ check_access_new('package_delete',context, data_dict)
rev = model.repo.new_revision()
rev.author = user
@@ -29,13 +29,13 @@
model.repo.commit()
-def package_relationship_delete(context):
+def package_relationship_delete(context, data_dict):
model = context['model']
user = context['user']
- id = context["id"]
- id2 = context["id2"]
- rel = context["rel"]
+ id = data_dict['id']
+ id2 = data_dict['id2']
+ rel = data_dict['rel']
pkg1 = model.Package.get(id)
pkg2 = model.Package.get(id2)
@@ -44,7 +44,7 @@
if not pkg2:
return NotFound('Second package named in address was not found.')
- check_access_new('package_relationship_delete', context)
+ check_access_new('package_relationship_delete', context, data_dict)
existing_rels = pkg1.get_relationships_with(pkg2, rel)
if not existing_rels:
@@ -54,7 +54,7 @@
revisioned_details = 'Package Relationship: %s %s %s' % (id, rel, id2)
context['relationship'] = relationship
- check_access_new('relationship_delete', context)
+ check_access_new('relationship_delete', context, data_dict)
rev = model.repo.new_revision()
rev.author = user
@@ -63,20 +63,20 @@
relationship.delete()
model.repo.commit()
-def group_delete(context):
+def group_delete(context, data_dict):
model = context['model']
user = context['user']
- id = context["id"]
+ id = data_dict['id']
group = model.Group.get(id)
- context["group"] = group
+ context['group'] = group
if group is None:
raise NotFound('Group was not found.')
revisioned_details = 'Group: %s' % group.name
- check_access_new('group_delete', context)
+ check_access_new('group_delete', context, data_dict)
rev = model.repo.new_revision()
rev.author = user
--- a/ckan/logic/auth/delete.py Fri Aug 05 18:17:13 2011 +0100
+++ b/ckan/logic/auth/delete.py Mon Aug 08 15:48:28 2011 +0100
@@ -7,13 +7,18 @@
def package_delete(context, data_dict):
model = context['model']
user = context['user']
- id = context['id']
- pkg = model.Package.get(id)
+ if not 'package' in context:
+ id = data_dict.get('id',None)
+ package = model.Package.get(id)
+ if not package:
+ raise NotFound
+ else:
+ package = context['package']
#TODO: model.Action.CHANGE_STATE or model.Action.PURGE?
- authorized = check_access(pkg, model.Action.PURGE, context)
+ authorized = check_access(package, model.Action.PURGE, context)
if not authorized:
- return {'success': False, 'msg': _('User %s not authorized to delete package %s') % (str(user),id)}
+ return {'success': False, 'msg': _('User %s not authorized to delete package %s') % (str(user),package.id)}
else:
return {'success': True}
@@ -25,22 +30,26 @@
user = context['user']
relationship = context['relationship']
- authorized = check_access(realtionship, model.Action.PURGE, context)
+ authorized = check_access(relationship, model.Action.PURGE, context)
if not authorized:
- return {'success': False, 'msg': _('User %s not authorized to delete relationship %s') % (str(user),id)}
+ return {'success': False, 'msg': _('User %s not authorized to delete relationship %s') % (str(user),relationship.id)}
else:
return {'success': True}
def group_delete(context, data_dict):
model = context['model']
user = context['user']
- #group = context['group']
- id = context['id']
- pkg = model.Group.get(id)
+ if not 'group' in context:
+ id = data_dict.get('id',None)
+ group = model.Group.get(id)
+ if not group:
+ raise NotFound
+ else:
+ group = context['group']
authorized = check_access(group, model.Action.PURGE, context)
if not authorized:
- return {'success': False, 'msg': _('User %s not authorized to delete group %s') % (str(user),id)}
+ return {'success': False, 'msg': _('User %s not authorized to delete group %s') % (str(user),group.id)}
else:
return {'success': True}
--- a/ckan/new_authz.py Fri Aug 05 18:17:13 2011 +0100
+++ b/ckan/new_authz.py Mon Aug 08 15:48:28 2011 +0100
@@ -24,7 +24,7 @@
# Rather than writing them out in full will use __import__
# to load anything from ckan.auth that looks like it might
# be an authorisation function
- for auth_module_name in ['get', 'create', 'update']:
+ for auth_module_name in ['get', 'create', 'update','delete']:
module_path = 'ckan.logic.auth.'+auth_module_name
try:
module = __import__(module_path)
@@ -37,6 +37,7 @@
for k, v in module.__dict__.items():
if not k.startswith('_'):
_auth_functions[k] = v
+
# Then overwrite them with any specific ones in the plugins:
resolved_auth_function_plugins = {}
fetched_auth_functions = {}
http://bitbucket.org/okfn/ckan/changeset/1f70a265f98d/
changeset: 1f70a265f98d
branch: feature-1253-authz-refactor
user: amercader
date: 2011-08-08 16:56:56
summary: [merge] from default
affected #: 38 files (1.6 MB)
--- a/.hgtags Mon Aug 08 15:48:28 2011 +0100
+++ b/.hgtags Mon Aug 08 15:56:56 2011 +0100
@@ -32,3 +32,4 @@
d83e20d807a6d5587ae5adef49e31fe48c906f9a ckan-1.3.3
3a59aa5b63d06dde77424e3313433a1fb9eb1215 ckan-1.4
a394ca150daac6977114ce5d8c911f7d9896d1cd ckan-1.4.1
+445917818a652787cae2457f2d8d871fadfc39bb ckan-1.4.2
--- a/CHANGELOG.txt Mon Aug 08 15:48:28 2011 +0100
+++ b/CHANGELOG.txt Mon Aug 08 15:56:56 2011 +0100
@@ -1,19 +1,21 @@
CKAN CHANGELOG
++++++++++++++
-v1.4.2 2011-XX-XX
+v1.4.2 2011-08-05
=================
Major:
* Packages revisions can be marked as 'moderated' (#1141)
* Password reset facility (#1186/#1198)
Minor:
- * Viewing of a package at any revision (#1141)
+ * Viewing of a package at any revision (#1236)
* API POSTs can be of Content-Type "application/json" as alternative to existing "application/x-www-form-urlencoded" (#1206)
* Caching of static files (#1223)
Bug fixes:
* When you removed last row of resource table, you could't add it again - since 1.0 (#1215)
+ * Adding a tag to package that had it previously didn't work - since 1.4.1 in UI and 1.4.0 in API (#1239)
+ * Search index was not updated if you added a package to a group - since 1.1 (#1140)
* Exception if you had any Groups and migrated between CKAN v1.0.2 to v1.2 (migration 29) - since v1.0.2 (#1205)
* API Package edit requests returned the Package in a different format to usual - since 1.4 (#1214)
* API error responses were not all JSON format and didn't have correct Content-Type (#1214)
--- a/ckan/controllers/api.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/controllers/api.py Mon Aug 08 15:56:56 2011 +0100
@@ -133,6 +133,9 @@
def action(self, logic_function):
function = get_action(logic_function)
+ if not function:
+ return self._finish_bad_request(
+ gettext('Action name not known: %s') % str(logic_function))
context = {'model': model, 'session': model.Session, 'user': c.user}
model.Session()._context = context
--- a/ckan/controllers/home.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/controllers/home.py Mon Aug 08 15:56:56 2011 +0100
@@ -49,11 +49,10 @@
c.fields = []
c.package_count = query.count
c.latest_packages = current_package_list_with_resources({'model': model,
- 'user': c.user,
- 'limit': 5},
- {})
+ 'user': c.user},
+ {'limit': 5})
return render('home/index.html', cache_key=cache_key,
- cache_expire=cache_expires)
+ cache_expire=cache_expires)
def license(self):
return render('home/license.html', cache_expire=cache_expires)
--- a/ckan/controllers/user.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/controllers/user.py Mon Aug 08 15:56:56 2011 +0100
@@ -141,13 +141,18 @@
error_summary = e.error_summary
return self.new(data_dict, errors, error_summary)
- def edit(self, id, data=None, errors=None, error_summary=None):
+ def edit(self, id=None, data=None, errors=None, error_summary=None):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author,
'preview': 'preview' in request.params,
'save': 'save' in request.params,
'schema': self._edit_form_to_db_schema(),
}
+ if id is None:
+ if c.userobj:
+ id = c.userobj.id
+ else:
+ abort(400, _('No user specified'))
data_dict = {'id': id}
if (context['save'] or context['preview']) and not data:
@@ -167,6 +172,8 @@
except NotAuthorized:
abort(401, _('Unauthorized to edit user %s') % '')
+ except NotFound, e:
+ abort(404, _('User not found'))
user_obj = context.get('user_obj')
--- a/ckan/i18n/ckan.pot Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/i18n/ckan.pot Mon Aug 08 15:56:56 2011 +0100
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: ckan 1.4.1\n"
+"Project-Id-Version: ckan 1.4.2\n"
"Report-Msgid-Bugs-To: EMAIL at ADDRESS\n"
-"POT-Creation-Date: 2011-06-27 10:54+0100\n"
+"POT-Creation-Date: 2011-08-05 11:21+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,87 +17,95 @@
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.5\n"
-#: ckan/controllers/api.py:40 ckan/controllers/authorization_group.py:19
-#: ckan/controllers/group.py:50 ckan/controllers/home.py:23
-#: ckan/controllers/package.py:95 ckan/controllers/revision.py:22
+#: ckan/controllers/api.py:36 ckan/controllers/authorization_group.py:19
+#: ckan/controllers/group.py:50 ckan/controllers/home.py:24
+#: ckan/controllers/package.py:100 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 ""
-#: ckan/controllers/api.py:96
+#: ckan/controllers/api.py:94
msgid "Access denied"
msgstr ""
-#: ckan/controllers/api.py:102
+#: ckan/controllers/api.py:100
msgid "Not found"
msgstr ""
-#: ckan/controllers/api.py:144
+#: ckan/controllers/api.py:108
+msgid "Bad request"
+msgstr ""
+
+#: ckan/controllers/api.py:150
#, python-format
msgid "Cannot list entity of this type: %s"
msgstr ""
-#: ckan/controllers/api.py:175
+#: ckan/controllers/api.py:180
#, python-format
msgid "Cannot read entity of this type: %s"
msgstr ""
-#: ckan/controllers/api.py:209 ckan/controllers/api.py:255
+#: ckan/controllers/api.py:214 ckan/controllers/api.py:261
#, python-format
msgid "JSON Error: %s"
msgstr ""
-#: ckan/controllers/api.py:216
+#: ckan/controllers/api.py:221
#, python-format
msgid "Cannot create new entity of this type: %s %s"
msgstr ""
-#: ckan/controllers/api.py:232 ckan/controllers/api.py:277
+#: ckan/controllers/api.py:238 ckan/controllers/api.py:283
#: ckan/controllers/group.py:161 ckan/controllers/group.py:179
-#: ckan/controllers/package.py:370 ckan/controllers/package.py:397
+#: ckan/controllers/package.py:437 ckan/controllers/package.py:469
msgid "Integrity Error"
msgstr ""
-#: ckan/controllers/api.py:261
+#: ckan/controllers/api.py:267
#, python-format
msgid "Cannot update entity of this type: %s"
msgstr ""
-#: ckan/controllers/api.py:298
+#: ckan/controllers/api.py:304
#, python-format
msgid "Cannot delete entity of this type: %s %s"
msgstr ""
-#: ckan/controllers/api.py:321
+#: ckan/controllers/api.py:327
+msgid "No revision specified"
+msgstr ""
+
+#: ckan/controllers/api.py:331
#, python-format
msgid "There is no revision with id: %s"
msgstr ""
-#: ckan/controllers/api.py:332
+#: ckan/controllers/api.py:341
msgid "Missing search term ('since_id=UUID' or 'since_time=TIMESTAMP')"
msgstr ""
-#: ckan/controllers/api.py:340
+#: ckan/controllers/api.py:349
#, python-format
msgid "Could not read parameters: %r"
msgstr ""
-#: ckan/controllers/api.py:377
+#: ckan/controllers/api.py:386
#, python-format
msgid "Bad search option: %s"
msgstr ""
-#: ckan/controllers/api.py:380
+#: ckan/controllers/api.py:389
#, python-format
msgid "Unknown register: %s"
msgstr ""
-#: ckan/controllers/api.py:388
+#: ckan/controllers/api.py:397
msgid "Malformed qjson value"
msgstr ""
-#: ckan/controllers/api.py:397 ckan/lib/base.py:159
+#: ckan/controllers/api.py:406 ckan/lib/base.py:172
msgid "Request params must be in form of a json encoded dictionary."
msgstr ""
@@ -123,7 +131,7 @@
msgstr ""
#: ckan/controllers/authorization_group.py:155 ckan/controllers/group.py:194
-#: ckan/controllers/package.py:438
+#: ckan/controllers/package.py:510
#, python-format
msgid "User %r not authorized to edit %s authorizations"
msgstr ""
@@ -134,21 +142,21 @@
msgid "Unauthorized to read group %s"
msgstr ""
-#: ckan/controllers/group.py:140 ckan/controllers/package.py:341
-#: ckan/controllers/package_formalchemy.py:97
+#: ckan/controllers/group.py:140 ckan/controllers/package.py:357
+#: ckan/controllers/package_formalchemy.py:102
#, python-format
msgid "User %r not authorized to edit %s"
msgstr ""
#: 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
+#: ckan/controllers/package.py:196 ckan/controllers/package.py:232
+#: ckan/controllers/package.py:264 ckan/controllers/package.py:351
+#: ckan/controllers/package.py:381 ckan/controllers/package.py:435
+#: ckan/controllers/package.py:467 ckan/controllers/package.py:504
msgid "Package not found"
msgstr ""
-#: ckan/controllers/group.py:412 ckan/controllers/package.py:242
+#: ckan/controllers/group.py:412 ckan/controllers/package.py:257
msgid "Select two revisions before doing the comparison."
msgstr ""
@@ -160,46 +168,46 @@
msgid "Recent changes to CKAN Group: "
msgstr ""
-#: ckan/controllers/group.py:450 ckan/controllers/package.py:276
+#: ckan/controllers/group.py:450 ckan/controllers/package.py:291
msgid "Log message: "
msgstr ""
-#: ckan/controllers/home.py:73
+#: ckan/controllers/home.py:74
msgid "Invalid language specified"
msgstr ""
-#: ckan/controllers/home.py:74
+#: ckan/controllers/home.py:75
msgid "Language has been set to: English"
msgstr ""
-#: ckan/controllers/home.py:76
+#: ckan/controllers/home.py:77
msgid "No language given!"
msgstr ""
-#: 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
+#: ckan/controllers/package.py:198 ckan/controllers/package.py:234
+#: ckan/controllers/package.py:349 ckan/controllers/package.py:379
+#: ckan/controllers/package.py:433 ckan/controllers/package.py:465
#, python-format
msgid "Unauthorized to read package %s"
msgstr ""
-#: ckan/controllers/package.py:255
+#: ckan/controllers/package.py:270
msgid "CKAN Package Revision History"
msgstr ""
-#: ckan/controllers/package.py:257
+#: ckan/controllers/package.py:272
msgid "Recent changes to CKAN Package: "
msgstr ""
-#: ckan/controllers/package.py:301 ckan/controllers/package_formalchemy.py:21
+#: ckan/controllers/package.py:316 ckan/controllers/package_formalchemy.py:23
msgid "Unauthorized to create a package"
msgstr ""
-#: ckan/controllers/package.py:659
+#: ckan/controllers/package.py:731
msgid "Package Not Found"
msgstr ""
-#: ckan/controllers/package.py:666
+#: ckan/controllers/package.py:738
msgid "Rating value invalid"
msgstr ""
@@ -263,11 +271,38 @@
msgid "Your account has been updated."
msgstr ""
-#: ckan/controllers/user.py:201
+#: ckan/controllers/user.py:197
+#, python-format
+msgid "\"%s\" matched several users"
+msgstr ""
+
+#: ckan/controllers/user.py:200
+#, python-format
+msgid "No such user: %s"
+msgstr ""
+
+#: ckan/controllers/user.py:204
+msgid "Please check your inbox for a reset code."
+msgstr ""
+
+#: ckan/controllers/user.py:207
+#, python-format
+msgid "Could not send reset link: %s"
+msgstr ""
+
+#: ckan/controllers/user.py:216
+msgid "Invalid reset key. Please try again."
+msgstr ""
+
+#: ckan/controllers/user.py:224
+msgid "Your password has been reset."
+msgstr ""
+
+#: ckan/controllers/user.py:244
msgid "Your password must be 4 characters or longer."
msgstr ""
-#: ckan/controllers/user.py:203
+#: ckan/controllers/user.py:246
msgid "The passwords you entered do not match."
msgstr ""
@@ -357,8 +392,8 @@
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/logic/action/update.py:35 ckan/logic/action/update.py:37
+#: ckan/logic/action/update.py:47 ckan/logic/action/update.py:49
#: ckan/templates/group/new_group_form.html:41
#: ckan/templates/package/new_package_form.html:154
msgid "Extras"
@@ -499,7 +534,7 @@
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/logic/action/update.py:33 ckan/templates/package/new_package_form.html:67
#: ckan/templates/package/read_core.html:60
msgid "Resources"
msgstr ""
@@ -516,8 +551,8 @@
msgid "Detail"
msgstr ""
-#: ckan/forms/package.py:110 ckan/templates/_util.html:97
-#: ckan/templates/_util.html:110 ckan/templates/group/new_group_form.html:23
+#: ckan/forms/package.py:110 ckan/templates/_util.html:147
+#: ckan/templates/_util.html:160 ckan/templates/group/new_group_form.html:23
#: ckan/templates/package/new_package_form.html:19
msgid "Title"
msgstr ""
@@ -530,7 +565,7 @@
msgid "URL"
msgstr ""
-#: ckan/forms/package.py:111 ckan/templates/_util.html:282
+#: ckan/forms/package.py:111 ckan/templates/_util.html:332
#: ckan/templates/group/history.html:35 ckan/templates/package/history.html:41
#: ckan/templates/package/new_package_form.html:123
msgid "Author"
@@ -572,20 +607,59 @@
msgid "Key blank"
msgstr ""
-#: ckan/lib/base.py:152
+#: ckan/lib/base.py:154
#, python-format
msgid "Could not find the POST data: %r : %s"
msgstr ""
-#: ckan/lib/package_saver.py:40
+#: ckan/lib/base.py:162
+#, python-format
+msgid "Could not extract request body data: %s"
+msgstr ""
+
+#: ckan/lib/base.py:167
+msgid "No request body data"
+msgstr ""
+
+#: ckan/lib/mailer.py:21
+#, python-format
+msgid "Dear %s,"
+msgstr ""
+
+#: ckan/lib/mailer.py:34
+#, python-format
+msgid "%s <%s>"
+msgstr ""
+
+#: ckan/lib/mailer.py:57
+msgid "No recipient email address available!"
+msgstr ""
+
+#: ckan/lib/mailer.py:62
+#, python-format
+msgid ""
+"You have requested your password on %(site_title)s to be reset.\n"
+"\n"
+"Please click the following link to confirm this request:\n"
+"\n"
+" %(reset_link)s\n"
+msgstr ""
+
+#: ckan/lib/mailer.py:94 ckan/templates/user/login.html:26
+#: ckan/templates/user/perform_reset.html:6
+#: ckan/templates/user/perform_reset.html:18
+msgid "Reset your password"
+msgstr ""
+
+#: ckan/lib/package_saver.py:44
msgid "Cannot render package description"
msgstr ""
-#: ckan/lib/package_saver.py:44
+#: ckan/lib/package_saver.py:49
msgid "No web page given"
msgstr ""
-#: ckan/lib/package_saver.py:141 ckan/logic/validators.py:20
+#: ckan/lib/package_saver.py:151 ckan/logic/validators.py:20
msgid "No links are allowed in the log_message."
msgstr ""
@@ -595,39 +669,38 @@
msgstr ""
#: ckan/logic/validators.py:30 ckan/logic/validators.py:56
-#: ckan/logic/action/update.py:86
+#: ckan/logic/action/update.py:161
msgid "Package was not found."
msgstr ""
-#: ckan/logic/validators.py:41 ckan/logic/action/create.py:182
+#: ckan/logic/validators.py:41 ckan/logic/action/create.py:185
#, python-format
msgid "Package with name %r does not exist."
msgstr ""
-#: ckan/logic/action/create.py:56 ckan/logic/action/create.py:143
-#: ckan/logic/action/update.py:103 ckan/logic/action/update.py:189
+#: ckan/logic/action/create.py:56 ckan/logic/action/create.py:146
#, python-format
msgid "REST API: Create object %s"
msgstr ""
-#: ckan/logic/action/create.py:118
+#: ckan/logic/action/create.py:121
#, python-format
msgid "REST API: Create package relationship: %s %s %s"
msgstr ""
-#: ckan/logic/action/create.py:169
+#: ckan/logic/action/create.py:172
msgid "You must supply a package id or name (parameter \"package\")."
msgstr ""
-#: ckan/logic/action/create.py:171
+#: ckan/logic/action/create.py:174
msgid "You must supply a rating (parameter \"rating\")."
msgstr ""
-#: ckan/logic/action/create.py:176
+#: ckan/logic/action/create.py:179
msgid "Rating must be an integer value."
msgstr ""
-#: ckan/logic/action/create.py:180
+#: ckan/logic/action/create.py:183
#, python-format
msgid "Rating must be between %i and %i."
msgstr ""
@@ -642,19 +715,24 @@
msgid "REST API: Delete %s"
msgstr ""
-#: ckan/logic/action/update.py:27
+#: ckan/logic/action/update.py:33
msgid "Package resource(s) incomplete"
msgstr ""
-#: ckan/logic/action/update.py:29 ckan/logic/action/update.py:41
+#: ckan/logic/action/update.py:35 ckan/logic/action/update.py:47
msgid "Missing Value"
msgstr ""
-#: ckan/logic/action/update.py:64
+#: ckan/logic/action/update.py:70
msgid "Group was not found."
msgstr ""
-#: ckan/logic/action/update.py:123
+#: ckan/logic/action/update.py:180 ckan/logic/action/update.py:264
+#, python-format
+msgid "REST API: Update object %s"
+msgstr ""
+
+#: ckan/logic/action/update.py:200
#, python-format
msgid "REST API: Update package relationship: %s %s %s"
msgstr ""
@@ -705,93 +783,96 @@
msgstr ""
#: ckan/templates/_util.html:65 ckan/templates/_util.html:70
+#: ckan/templates/_util.html:115 ckan/templates/_util.html:120
msgid "This package satisfies the Open Definition."
msgstr ""
-#: ckan/templates/_util.html:66 ckan/templates/package/read.html:75
+#: ckan/templates/_util.html:66 ckan/templates/_util.html:116
+#: ckan/templates/package/read.html:75
msgid "[Open Data]"
msgstr ""
-#: ckan/templates/_util.html:71 ckan/templates/package/read.html:79
+#: ckan/templates/_util.html:71 ckan/templates/_util.html:121
+#: ckan/templates/package/read.html:79
msgid "[Open Content]"
msgstr ""
-#: ckan/templates/_util.html:78
+#: ckan/templates/_util.html:78 ckan/templates/_util.html:128
msgid "Not Openly Licensed"
msgstr ""
-#: ckan/templates/_util.html:97
+#: ckan/templates/_util.html:147
msgid "Number of packages"
msgstr ""
-#: ckan/templates/_util.html:97 ckan/templates/group/new_group_form.html:27
+#: ckan/templates/_util.html:147 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 ""
-#: ckan/templates/_util.html:110
+#: ckan/templates/_util.html:160
msgid "Number of members"
msgstr ""
-#: ckan/templates/_util.html:130
+#: ckan/templates/_util.html:180
msgid "View package resources"
msgstr ""
-#: ckan/templates/_util.html:130
+#: ckan/templates/_util.html:180
msgid "DOWNLOAD"
msgstr ""
-#: ckan/templates/_util.html:133
+#: ckan/templates/_util.html:183
msgid "No downloadable resources."
msgstr ""
-#: ckan/templates/_util.html:151
+#: ckan/templates/_util.html:201
msgid "no ratings yet"
msgstr ""
-#: ckan/templates/_util.html:152
+#: ckan/templates/_util.html:202
msgid ""
"–\n"
" rate it now"
msgstr ""
-#: ckan/templates/_util.html:170 ckan/templates/_util.html:240
+#: ckan/templates/_util.html:220 ckan/templates/_util.html:290
msgid "User"
msgstr ""
-#: ckan/templates/_util.html:205 ckan/templates/_util.html:261
+#: ckan/templates/_util.html:255 ckan/templates/_util.html:311
msgid "User Group"
msgstr ""
-#: ckan/templates/_util.html:282 ckan/templates/group/history.html:35
+#: ckan/templates/_util.html:332 ckan/templates/group/history.html:35
#: ckan/templates/package/history.html:41 ckan/templates/revision/read.html:5
msgid "Revision"
msgstr ""
-#: ckan/templates/_util.html:282 ckan/templates/group/history.html:35
+#: ckan/templates/_util.html:332 ckan/templates/group/history.html:35
#: ckan/templates/package/history.html:41
msgid "Timestamp"
msgstr ""
-#: ckan/templates/_util.html:282
+#: ckan/templates/_util.html:332
msgid "Entity"
msgstr ""
-#: ckan/templates/_util.html:282 ckan/templates/group/history.html:35
+#: ckan/templates/_util.html:332 ckan/templates/group/history.html:35
#: ckan/templates/package/history.html:41
msgid "Log Message"
msgstr ""
-#: ckan/templates/_util.html:306 ckan/templates/group/new_group_form.html:49
+#: ckan/templates/_util.html:356 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 ""
-#: ckan/templates/_util.html:309 ckan/templates/revision/read.html:23
+#: ckan/templates/_util.html:359 ckan/templates/revision/read.html:23
msgid "Undelete"
msgstr ""
@@ -807,8 +888,8 @@
msgid "Logout"
msgstr ""
-#: ckan/templates/layout_base.html:80 ckan/templates/user/login.html:34
-#: ckan/templates/user/login.html:47
+#: ckan/templates/layout_base.html:80 ckan/templates/user/login.html:35
+#: ckan/templates/user/login.html:48
msgid "Login"
msgstr ""
@@ -896,7 +977,7 @@
msgid "Contact Us"
msgstr ""
-#: ckan/templates/layout_base.html:210 ckan/templates/user/login.html:26
+#: ckan/templates/layout_base.html:210 ckan/templates/user/login.html:27
msgid "Privacy Policy"
msgstr ""
@@ -955,7 +1036,7 @@
#: 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
+#: ckan/templates/user/edit.html:45 ckan/templates/user/perform_reset.html:27
msgid "Save"
msgstr ""
@@ -2031,12 +2112,13 @@
msgid "Change your password"
msgstr ""
-#: ckan/templates/user/edit.html:32 ckan/templates/user/login.html:43
-#: ckan/templates/user/register.html:37
+#: ckan/templates/user/edit.html:32 ckan/templates/user/login.html:44
+#: ckan/templates/user/perform_reset.html:19 ckan/templates/user/register.html:37
msgid "Password:"
msgstr ""
-#: ckan/templates/user/edit.html:35 ckan/templates/user/register.html:40
+#: ckan/templates/user/edit.html:35 ckan/templates/user/perform_reset.html:22
+#: ckan/templates/user/register.html:40
msgid "Password (repeat):"
msgstr ""
@@ -2077,23 +2159,27 @@
msgid "Join CKAN to contribute packages under your own name."
msgstr ""
-#: ckan/templates/user/login.html:31
+#: ckan/templates/user/login.html:32
msgid "Login - User"
msgstr ""
-#: ckan/templates/user/login.html:40 ckan/templates/user/register.html:28
+#: ckan/templates/user/login.html:41 ckan/templates/user/register.html:28
msgid "Login:"
msgstr ""
-#: ckan/templates/user/login.html:54
+#: ckan/templates/user/login.html:49
+msgid "Forgot your password?"
+msgstr ""
+
+#: ckan/templates/user/login.html:56
msgid "Login using Open ID"
msgstr ""
-#: ckan/templates/user/login.html:56
+#: ckan/templates/user/login.html:58
msgid "Please click your account provider:"
msgstr ""
-#: ckan/templates/user/login.html:64
+#: ckan/templates/user/login.html:66
msgid ""
"OpenID is service that allows you to log-on to many different websites using "
"a single identity.\n"
@@ -2101,11 +2187,11 @@
"account]."
msgstr ""
-#: ckan/templates/user/login.html:71
+#: ckan/templates/user/login.html:73
msgid "Don't have an OpenID?"
msgstr ""
-#: ckan/templates/user/login.html:72
+#: ckan/templates/user/login.html:74
msgid ""
"OpenID is service that allows you to log-on to many different websites\n"
" using a single identity. Find out [1:more\n"
@@ -2204,3 +2290,17 @@
msgid "Sign up"
msgstr ""
+#: ckan/templates/user/request_reset.html:6
+#: ckan/templates/user/request_reset.html:22
+#: ckan/templates/user/request_reset.html:28
+msgid "Reset password"
+msgstr ""
+
+#: ckan/templates/user/request_reset.html:13
+msgid "Request a password reset"
+msgstr ""
+
+#: ckan/templates/user/request_reset.html:23
+msgid "User name:"
+msgstr ""
+
--- a/ckan/lib/base.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/lib/base.py Mon Aug 08 15:56:56 2011 +0100
@@ -99,6 +99,13 @@
if c.user:
c.user = c.user.decode('utf8')
c.userobj = model.User.by_name(c.user)
+ if c.userobj is None:
+ # This occurs when you are logged in with openid, clean db
+ # and then restart i.e. only really for testers. There is no
+ # user object, so even though repoze thinks you are logged in
+ # and your cookie has ckan_display_name, we need to force user
+ # to login again to get the User object.
+ c.user = None
else:
c.userobj = self._get_user_for_apikey()
if c.userobj is not None:
--- a/ckan/lib/dictization/model_save.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/lib/dictization/model_save.py Mon Aug 08 15:56:56 2011 +0100
@@ -139,12 +139,19 @@
tag_package_tag = dict((package_tag.tag, package_tag)
for package_tag in
package.package_tag_all)
+
+ tag_package_tag_inactive = dict(
+ [ (tag,pt) for tag,pt in tag_package_tag.items() if
+ pt.state in ['deleted', 'pending-deleted'] ]
+ )
tags = set()
for tag_dict in tag_dicts:
obj = table_dict_save(tag_dict, model.Tag, context)
tags.add(obj)
+ # 3 cases
+ # case 1: currently active but not in new list
for tag in set(tag_package_tag.keys()) - tags:
package_tag = tag_package_tag[tag]
if pending and package_tag.state <> 'deleted':
@@ -152,12 +159,19 @@
else:
package_tag.state = 'deleted'
+ # in new list but never used before
for tag in tags - set(tag_package_tag.keys()):
state = 'pending' if pending else 'active'
package_tag_obj = model.PackageTag(package, tag, state)
session.add(package_tag_obj)
tag_package_tag[tag] = package_tag_obj
+ # in new list and already used but in deleted state
+ for tag in tags.intersection(set(tag_package_tag_inactive.keys())):
+ state = 'pending' if pending else 'active'
+ package_tag = tag_package_tag[tag]
+ package_tag.state = state
+
package.package_tag_all[:] = tag_package_tag.values()
def package_group_list_save(group_dicts, package, context):
--- a/ckan/lib/helpers.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/lib/helpers.py Mon Aug 08 15:56:56 2011 +0100
@@ -5,7 +5,9 @@
Consists of functions to typically be used within templates, but also
available to Controllers. This module is available to templates as 'h'.
"""
-from datetime import datetime
+import datetime
+import re
+
from webhelpers.html import escape, HTML, literal, url_escape
from webhelpers.html.tools import mail_to
from webhelpers.html.tags import *
@@ -28,8 +30,7 @@
import json
except ImportError:
import simplejson as json
-
-ISO_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.%f'
+
class Message(object):
"""A message returned by ``Flash.pop_messages()``.
@@ -210,11 +211,11 @@
'''
from ckan import model
date_format = '%Y-%m-%d %H:%M'
- if isinstance(datetime_, datetime):
+ if isinstance(datetime_, datetime.datetime):
return datetime_.strftime(date_format)
elif isinstance(datetime_, basestring):
try:
- datetime_ = model.strptimestamp(datetime_)
+ datetime_ = date_str_to_datetime(datetime_)
except TypeError:
return ''
except ValueError:
@@ -223,8 +224,20 @@
else:
return ''
-def date_str_to_datetime(date_str, format=ISO_DATE_FORMAT):
- return datetime.strptime(date_str, format)
+def datetime_to_date_str(datetime_):
+ '''Takes a datetime.datetime object and returns a string of it
+ in ISO format.
+ '''
+ return datetime_.isoformat()
-def time_ago_in_words_from_str(date_str, format=ISO_DATE_FORMAT, granularity='month'):
- return date.time_ago_in_words(datetime.strptime(date_str, format), granularity=granularity)
+def date_str_to_datetime(date_str):
+ '''Takes an ISO format timestamp and returns the equivalent
+ datetime.datetime object.
+ '''
+ # Doing this split is more accepting of input variations than doing
+ # a strptime. Also avoids problem with Python 2.5 not having %f.
+ return datetime.datetime(*map(int, re.split('[^\d]', date_str)))
+
+def time_ago_in_words_from_str(date_str, granularity='month'):
+ return date.time_ago_in_words(date_str_to_datetime(date_str), granularity=granularity)
+
--- a/ckan/lib/search/worker.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/lib/search/worker.py Mon Aug 08 15:56:56 2011 +0100
@@ -1,7 +1,9 @@
import logging
+import ckan.model as model
from ckan.model import DomainObjectOperation
from ckan.plugins import SingletonPlugin, implements, IDomainObjectModification
+from ckan.lib.dictization.model_dictize import package_to_api1
from common import SearchError
log = logging.getLogger(__name__)
@@ -31,10 +33,11 @@
implements(IDomainObjectModification, inherit=True)
def notify(self, entity, operation):
-
- if hasattr(entity, 'as_dict') and operation != DomainObjectOperation.deleted:
+
+ if operation != DomainObjectOperation.deleted:
dispatch_by_operation(entity.__class__.__name__,
- entity.as_dict(), operation)
+ package_to_api1(entity, {'model': model}),
+ operation)
elif operation == DomainObjectOperation.deleted:
dispatch_by_operation(entity.__class__.__name__,
{'id': entity.id}, operation)
--- a/ckan/logic/action/get.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/logic/action/get.py Mon Aug 08 15:56:56 2011 +0100
@@ -624,7 +624,10 @@
package_dict['resources'] = []
license_id = package_dict['license_id']
if license_id:
- isopen = model.Package.get_license_register()[license_id].isopen()
+ try:
+ isopen = model.Package.get_license_register()[license_id].isopen()
+ except KeyError:
+ isopen = False
package_dict['isopen'] = isopen
else:
package_dict['isopen'] = False
--- a/ckan/logic/action/update.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/logic/action/update.py Mon Aug 08 15:56:56 2011 +0100
@@ -8,6 +8,7 @@
from ckan.logic import check_access_new, check_access
from ckan.lib.base import _
+from vdm.sqlalchemy.base import SQLAlchemySession
from ckan.lib.dictization.model_dictize import (package_dictize,
package_to_api1,
package_to_api2,
@@ -84,7 +85,9 @@
latest_rev.current = True
if latest_rev.state in ('pending-deleted', 'deleted'):
latest_rev.state = 'deleted'
+ latest_rev.continuity.state = 'deleted'
else:
+ latest_rev.continuity.state = 'active'
latest_rev.state = 'active'
session.add(latest_rev)
@@ -104,6 +107,7 @@
model = context['model']
session = model.Session
+ SQLAlchemySession.setattr(session, 'revisioning_disabled', True)
id = data_dict["id"]
pkg = model.Package.get(id)
--- a/ckan/model/__init__.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/model/__init__.py Mon Aug 08 15:56:56 2011 +0100
@@ -23,6 +23,7 @@
from changeset import Changeset, Change, Changemask
import ckan.migration
from ckan.lib.helpers import OrderedDict
+from vdm.sqlalchemy.base import SQLAlchemySession
# set up in init_model after metadata is bound
version_table = None
@@ -180,6 +181,63 @@
metadata.reflect()
return bool(metadata.tables)
+ def purge_revision(self, revision, leave_record=False):
+ '''Purge all changes associated with a revision.
+
+ @param leave_record: if True leave revision in existence but change message
+ to "PURGED: {date-time-of-purge}". If false delete revision object as
+ well.
+
+ Summary of the Algorithm
+ ------------------------
+
+ 1. list all RevisionObjects affected by this revision
+ 2. check continuity objects and cascade on everything else ?
+ 1. crudely get all object revisions associated with this
+ 2. then check whether this is the only revision and delete the
+ continuity object
+
+ 3. ALTERNATIVELY delete all associated object revisions then do a
+ select on continutity to check which have zero associated revisions
+ (should only be these ...)
+ '''
+ to_purge = []
+ SQLAlchemySession.setattr(self.session, 'revisioning_disabled', True)
+ self.session.autoflush = False
+ for o in self.versioned_objects:
+ revobj = o.__revision_class__
+ items = self.session.query(revobj).filter_by(revision=revision).all()
+ for item in items:
+ continuity = item.continuity
+
+ if continuity.revision == revision: # need to change continuity
+ trevobjs = self.session.query(revobj).join('revision'). filter(
+ revobj.continuity==continuity
+ ).order_by(Revision.timestamp.desc()).all()
+ if len(trevobjs) == 0:
+ raise Exception('Should have at least one revision.')
+ if len(trevobjs) == 1:
+ to_purge.append(continuity)
+ else:
+ self.revert(continuity, trevobjs[1])
+ for num, obj in enumerate(trevobjs):
+ if num == 0:
+ continue
+ if 'pending' not in obj.state:
+ obj.current = True
+ self.session.add(obj)
+ break
+ # now delete revision object
+ self.session.delete(item)
+ for cont in to_purge:
+ self.session.delete(cont)
+ if leave_record:
+ import datetime
+ revision.message = u'PURGED: %s' % datetime.datetime.now()
+ else:
+ self.session.delete(revision)
+ self.commit_and_remove()
+
repo = Repository(metadata, Session,
versioned_objects=[Package, PackageTag, Resource, ResourceGroup, PackageExtra, PackageGroup, Group]
@@ -226,7 +284,7 @@
and 7 (see datetime constructor).
raises ValueError if any of the numbers are out of range.
'''
-
+ # TODO: METHOD DEPRECATED - use ckan.lib.helpers.date_str_to_datetime
import datetime, re
return datetime.datetime(*map(int, re.split('[^\d]', s)))
@@ -234,6 +292,7 @@
'''Takes a datetime.datetime and returns it as an ISO string. For
a pretty printed string, use ckan.lib.helpers.render_datetime.
'''
+ # TODO: METHOD DEPRECATED - use ckan.lib.helpers.datetime_to_date_str
return t.isoformat()
def revision_as_dict(revision, include_packages=True, include_groups=True,ref_package_by='name'):
--- a/ckan/model/group.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/model/group.py Mon Aug 08 15:56:56 2011 +0100
@@ -38,7 +38,8 @@
class PackageGroup(vdm.sqlalchemy.RevisionedObjectMixin,
vdm.sqlalchemy.StatefulObjectMixin,
DomainObject):
- pass
+ def related_packages(self):
+ return [self.package]
class Group(vdm.sqlalchemy.RevisionedObjectMixin,
vdm.sqlalchemy.StatefulObjectMixin,
@@ -154,6 +155,8 @@
PackageGroupRevision = vdm.sqlalchemy.create_object_version(mapper, PackageGroup,
package_group_revision_table)
+PackageGroupRevision.related_packages = lambda self: [self.continuity.package]
+
from vdm.sqlalchemy.base import add_stateful_versioned_m2m
#vdm.sqlalchemy.add_stateful_versioned_m2m(Package, PackageGroup, 'groups', 'group',
--- a/ckan/model/modification.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/model/modification.py Mon Aug 08 15:56:56 2011 +0100
@@ -52,12 +52,10 @@
related_packages = obj.related_packages()
except AttributeError:
continue
- if 'pending' in obj.state:
- continue
# this is needed to sort out vdm bug where pkg.as_dict does not
# work when the package is deleted.
for package in related_packages:
- if package not in deleted | new:
+ if package and package not in deleted | new:
changed_pkgs.add(package)
for obj in changed_pkgs:
self.notify(obj, DomainObjectOperation.changed)
--- a/ckan/model/package_extra.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/model/package_extra.py Mon Aug 08 15:56:56 2011 +0100
@@ -51,6 +51,8 @@
PackageExtraRevision= vdm.sqlalchemy.create_object_version(mapper, PackageExtra,
extra_revision_table)
+PackageExtraRevision.related_packages = lambda self: [self.continuity.package]
+
def _create_extra(key, value):
return PackageExtra(key=unicode(key), value=value)
--- a/ckan/model/package_mapping.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/model/package_mapping.py Mon Aug 08 15:56:56 2011 +0100
@@ -33,4 +33,9 @@
PackageRevision = vdm.sqlalchemy.create_object_version(mapper, Package,
package_revision_table)
+def related_packages(self):
+ return [self.continuity]
+PackageRevision.related_packages = related_packages
+
+
--- a/ckan/model/resource.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/model/resource.py Mon Aug 08 15:56:56 2011 +0100
@@ -203,6 +203,9 @@
ResourceGroupRevision = vdm.sqlalchemy.create_object_version(
mapper, ResourceGroup, resource_group_revision_table)
+ResourceGroupRevision.related_packages = lambda self: [self.continuity.package]
+ResourceRevision.related_packages = lambda self: [self.continuity.resouce_group.package]
+
import vdm.sqlalchemy.stateful
# TODO: move this into vdm
def add_stateful_m21(object_to_alter, m21_property_name,
--- a/ckan/model/tag.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/model/tag.py Mon Aug 08 15:56:56 2011 +0100
@@ -132,6 +132,7 @@
PackageTagRevision = vdm.sqlalchemy.create_object_version(mapper, PackageTag,
package_tag_revision_table)
+PackageTagRevision.related_packages = lambda self: [self.continuity.package]
from vdm.sqlalchemy.base import add_stateful_versioned_m2m
vdm.sqlalchemy.add_stateful_versioned_m2m(Package, PackageTag, 'tags', 'tag',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/public/scripts/formatautocomplete.js Mon Aug 08 15:56:56 2011 +0100
@@ -0,0 +1,37 @@
+(function($){
+ var url = "";
+
+ function extractDataAttributes(){
+ var el = $(this);
+ $.each(this.attributes, function(){
+ // get the autocomplete API URL
+ if(this.name === 'data-format-autocomplete-url'){
+ url = this.value;
+ }
+ });
+ }
+
+ function autoCompleteList(request, response){
+ var requestData = {'incomplete': request.term};
+
+ $.ajax({
+ url: url,
+ data: requestData,
+ dataType: 'jsonp',
+ type: 'get',
+ jsonpCallback: 'callback',
+ success: function(json){
+ var formats = [];
+ $.each(json["ResultSet"]["Result"], function(){
+ formats.push(this["Format"]);
+ });
+ response(formats);
+ },
+ });
+ }
+
+ $(document).ready(function(){
+ $('.format-autocomplete').focus(extractDataAttributes)
+ .autocomplete({source: autoCompleteList});
+ });
+})(jQuery);
--- a/ckan/templates/package/edit.html Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/templates/package/edit.html Mon Aug 08 15:56:56 2011 +0100
@@ -17,6 +17,9 @@
<!-- Tagcomplete --><script type="text/javascript" src="${g.site_url}/scripts/tagcomplete.js"></script><link rel="stylesheet" href="${g.site_url}/css/tagcomplete.css" />
+
+ <!-- Format field autocomplete -->
+ <script type="text/javascript" src="${g.site_url}/scripts/formatautocomplete.js"></script></py:def><div py:match="content" class="package">
--- a/ckan/templates/package/new_package_form.html Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/templates/package/new_package_form.html Mon Aug 08 15:56:56 2011 +0100
@@ -78,8 +78,11 @@
<py:for each="num, res in enumerate(data.get('resources', []) + [{}])"><tr><py:for each="col in c.resource_columns">
- <td class="resource-${col}">
- <input name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="${'medium-width' if col=='description' else 'short'}" />
+ <td py:choose="" class="resource-${col}">
+ <input py:when="col == 'format'" name="resources__${num}__${col}"
+ type="text" value="${res.get(col, '')}" class="format-autocomplete short"
+ data-format-autocomplete-url="/api/2/util/resource/format_autocomplete" />
+ <input py:otherwise="" name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="${'medium-width' if col=='description' else 'short'}" /></td></py:for><td class="resource-id"><input name="resources__${num}__id" type="hidden" value="${res.get('id', '')}" /></td>
--- a/ckan/tests/functional/api/model/test_package.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/tests/functional/api/model/test_package.py Mon Aug 08 15:56:56 2011 +0100
@@ -419,6 +419,37 @@
# - title
assert len(package.extras) == 1, package.extras
+ def test_entity_update_readd_tag(self):
+ name = self.package_fixture_data['name']
+ old_fixture_data = {
+ 'name': name,
+ 'tags': ['tag1', 'tag2']
+ }
+ new_fixture_data = {
+ 'name': name,
+ 'tags': ['tag1']
+ }
+ self.create_package_roles_revision(old_fixture_data)
+ offset = self.package_offset(name)
+ params = '%s=1' % self.dumps(new_fixture_data)
+ res = self.app.post(offset, params=params, status=self.STATUS_200_OK,
+ extra_environ=self.extra_environ)
+
+ # Check the returned package is as expected
+ pkg = self.loads(res.body)
+ assert_equal(pkg['name'], new_fixture_data['name'])
+ assert_equal(pkg['tags'], ['tag1'])
+
+ package = self.get_package_by_name(new_fixture_data['name'])
+ assert len(package.tags) == 1, package.tags
+
+ # now reinstate the tag
+ params = '%s=1' % self.dumps(old_fixture_data)
+ res = self.app.post(offset, params=params, status=self.STATUS_200_OK,
+ extra_environ=self.extra_environ)
+ pkg = self.loads(res.body)
+ assert_equal(pkg['tags'], ['tag1', 'tag2'])
+
def test_entity_update_conflict(self):
package1_name = self.package_fixture_data['name']
package1_data = {'name': package1_name}
--- a/ckan/tests/functional/api/test_action.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/tests/functional/api/test_action.py Mon Aug 08 15:56:56 2011 +0100
@@ -1,5 +1,6 @@
import json
from pprint import pprint, pformat
+from nose.tools import assert_equal
from ckan.lib.create_test_data import CreateTestData
import ckan.model as model
@@ -146,6 +147,27 @@
assert 'apikey' in result
assert 'reset_key' in result
+ def test_05_user_show_edits(self):
+ postparams = '%s=1' % json.dumps({'id':'tester'})
+ res = self.app.post('/api/action/user_show', params=postparams)
+ res_obj = json.loads(res.body)
+ assert res_obj['help'] == 'Shows user details'
+ assert res_obj['success'] == True
+ result = res_obj['result']
+ assert result['name'] == 'tester'
+ assert_equal(result['about'], None)
+ assert result['number_of_edits'] >= 1
+ edit = result['activity'][-1] # first edit chronologically
+ assert_equal(edit['author'], 'tester')
+ assert 'timestamp' in edit
+ assert_equal(edit['state'], 'active')
+ assert_equal(edit['approved_timestamp'], None)
+ assert_equal(set(edit['groups']), set(('roger', 'david')))
+ assert_equal(edit['state'], 'active')
+ assert edit['message'].startswith('Creating test data.')
+ assert_equal(set(edit['packages']), set(('warandpeace', 'annakarenina')))
+ assert 'id' in edit
+
def test_06_tag_list(self):
postparams = '%s=1' % json.dumps({})
res = self.app.post('/api/action/tag_list', params=postparams)
@@ -185,6 +207,26 @@
assert 'packages' in result and len(result['packages']) == 3
assert [package['name'] for package in result['packages']].sort() == ['annakarenina', 'warandpeace', 'moo'].sort()
+ def test_07_tag_show_unknown_license(self):
+ # create a tagged package which has an invalid license
+ CreateTestData.create_arbitrary([{
+ 'name': u'tag_test',
+ 'tags': u'tolstoy',
+ 'license': 'never_heard_of_it',
+ }])
+ postparams = '%s=1' % json.dumps({'id':'tolstoy'})
+ res = self.app.post('/api/action/tag_show', params=postparams)
+ res_obj = json.loads(res.body)
+ assert res_obj['success'] == True
+ result = res_obj['result']
+ for pkg in result['packages']:
+ if pkg['name'] == 'tag_test':
+ break
+ else:
+ assert 0, 'tag_test not among packages'
+ assert_equal(pkg['license_id'], 'never_heard_of_it')
+ assert_equal(pkg['isopen'], False)
+
def test_08_user_create_not_authorized(self):
postparams = '%s=1' % json.dumps({'name':'test_create_from_action_api', 'password':'testpass'})
res = self.app.post('/api/action/user_create', params=postparams,
@@ -424,3 +466,11 @@
assert res_obj['result'][0]['name'] == 'joeadmin'
assert 'id','fullname' in res_obj['result'][0]
+ def test_17_bad_action(self):
+ #Empty query
+ postparams = '%s=1' % json.dumps({})
+ res = self.app.post('/api/action/bad_action_name', params=postparams,
+ status=400)
+ res_obj = json.loads(res.body)
+ assert_equal(res_obj, u'Bad request - Action name not known: bad_action_name')
+
--- a/ckan/tests/functional/test_package.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/tests/functional/test_package.py Mon Aug 08 15:56:56 2011 +0100
@@ -1646,7 +1646,8 @@
self.assert_not_equal(hash_7, hash_6)
def test_etags_in_response(self):
- c.user = 'test user'
+ c.user = 'annafan'
+ c.userobj = model.User.by_name(u'annafan')
res = self.app.get('/package/annakarenina',
extra_environ={'REMOTE_USER':c.user})
anna_hash = str(PackageController._pkg_cache_key(self.anna))
--- a/ckan/tests/functional/test_user.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/tests/functional/test_user.py Mon Aug 08 15:56:56 2011 +0100
@@ -62,6 +62,10 @@
offset = '/user/'
res = self.app.get(offset, status=302)
+ def test_user_read_me_without_id(self):
+ offset = '/user/me'
+ res = self.app.get(offset, status=302)
+
def test_user_read_without_id_but_logged_in(self):
user = model.User.by_name(u'annafan')
offset = '/user/'
@@ -412,6 +416,30 @@
main_res = self.main_div(res)
assert new_about in main_res, main_res
+ def test_user_edit_no_user(self):
+ offset = url_for(controller='user', action='edit', id=None)
+ res = self.app.get(offset, status=400)
+ assert 'No user specified' in res, res
+
+ def test_user_edit_unknown_user(self):
+ offset = url_for(controller='user', action='edit', id='unknown_person')
+ res = self.app.get(offset, status=404)
+ assert 'User not found' in res, res
+
+ def test_user_edit_not_logged_in(self):
+ # create user
+ username = 'testedit'
+ about = u'Test About'
+ user = model.User.by_name(unicode(username))
+ if not user:
+ model.Session.add(model.User(name=unicode(username), about=about,
+ password='letmein'))
+ model.repo.commit_and_remove()
+ user = model.User.by_name(unicode(username))
+
+ offset = url_for(controller='user', action='edit', id=username)
+ res = self.app.get(offset, status=302)
+
def test_edit_spammer(self):
# create user
username = 'testeditspam'
--- a/ckan/tests/lib/test_helpers.py Mon Aug 08 15:48:28 2011 +0100
+++ b/ckan/tests/lib/test_helpers.py Mon Aug 08 15:56:56 2011 +0100
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
import time
+import datetime
+from nose.tools import assert_equal
from ckan.tests import *
from ckan.lib import helpers as h
@@ -16,4 +18,35 @@
def test_extract_markdown(self):
assert "Data exposed" in h.markdown_extract(WITH_HTML)
- assert "collects information" in h.markdown_extract(WITH_UNICODE)
\ No newline at end of file
+ assert "collects information" in h.markdown_extract(WITH_UNICODE)
+
+ def test_render_datetime(self):
+ res = h.render_datetime(datetime.datetime(2008, 4, 13, 20, 40, 20, 123456))
+ assert_equal(res, '2008-04-13 20:40')
+
+ def test_render_datetime_but_from_string(self):
+ res = h.render_datetime('2008-04-13T20:40:20.123456')
+ assert_equal(res, '2008-04-13 20:40')
+
+ def test_render_datetime_blank(self):
+ res = h.render_datetime(None)
+ assert_equal(res, '')
+
+ def test_datetime_to_date_str(self):
+ res = h.datetime_to_date_str(datetime.datetime(2008, 4, 13, 20, 40, 20, 123456))
+ assert_equal(res, '2008-04-13T20:40:20.123456')
+
+ def test_date_str_to_datetime(self):
+ res = h.date_str_to_datetime('2008-04-13T20:40:20.123456')
+ assert_equal(res, datetime.datetime(2008, 4, 13, 20, 40, 20, 123456))
+
+ def test_date_str_to_datetime_without_microseconds(self):
+ # This occurs in ckan.net timestamps - not sure how they appeared
+ res = h.date_str_to_datetime('2008-04-13T20:40:20')
+ assert_equal(res, datetime.datetime(2008, 4, 13, 20, 40, 20))
+
+ def test_time_ago_in_words_from_str(self):
+ two_months_ago = datetime.datetime.now() - datetime.timedelta(days=65)
+ two_months_ago_str = h.datetime_to_date_str(two_months_ago)
+ res = h.time_ago_in_words_from_str(two_months_ago_str)
+ assert_equal(res, '2 months')
--- a/doc/api.rst Mon Aug 08 15:48:28 2011 +0100
+++ b/doc/api.rst Mon Aug 08 15:56:56 2011 +0100
@@ -535,3 +535,16 @@
::
{"ResultSet": {"Result": [{"Name": "russian"}]}}
+
+Similarly, there is an autocomplete API for the resource format field
+which is available at:
+
+::
+
+ /api/2/util/resource/format_autocomplete?incomplete=cs
+
+This returns:
+
+::
+
+ {"ResultSet": {"Result": [{"Format": "csv"}]}}
Binary file doc/images/virtualbox4-newvm.png has changed
Binary file doc/images/virtualbox5-vmtype.png has changed
Binary file doc/images/virtualbox6-vmloc.png has changed
Binary file doc/images/virtualbox7-startvm.png has changed
Binary file doc/images/virtualbox8-firstrun.png has changed
Binary file doc/images/virtualbox9-iso.png has changed
--- a/doc/install-from-package.rst Mon Aug 08 15:48:28 2011 +0100
+++ b/doc/install-from-package.rst Mon Aug 08 15:56:56 2011 +0100
@@ -4,9 +4,9 @@
This section describes how to install CKAN from packages. This is the recommended and by far the easiest way to install CKAN.
-Package install requires you to use 64-bit Ubuntu 10.04: either locally, through a virtual machine or Amazon EC2. Your options are as follows:
+Package install requires you to use Ubuntu 10.04: either locally, through a virtual machine or Amazon EC2. Your options are as follows:
-* Using 64-bit Ubuntu 10.04 directly.
+* Using Ubuntu 10.04 directly.
* :ref:`using-virtualbox`. This is suitable if you want to host your CKAN instance on a machine running any other OS.
* :ref:`using-amazon`. This is suitable if you want to host your CKAN instance in the cloud, on a ready-made Ubuntu OS.
@@ -17,7 +17,7 @@
Prepare your System
--------------------
-CKAN runs on 64-bit Ubuntu 10.04. If you are already using Ubuntu 10.04, you can continue straight to :ref:`run-package-installer`.
+CKAN runs on Ubuntu 10.04. If you are already using Ubuntu 10.04, you can continue straight to :ref:`run-package-installer`.
However, if you're not, you can either use VirtualBox to set up an Ubuntu VM on Windows, Linux, Macintosh and Solaris. Alternatively, you can use an Amazon EC2 instance.
@@ -36,7 +36,7 @@
Then download the installation files.
* `Download the VirtualBox installer <http://www.virtualbox.org/wiki/Downloads>`_.
-* `Download the Ubuntu image <http://www.ubuntu.com/download/ubuntu/download>`_ - make sure you choose Ubuntu 10.04 and 64-bit.
+* `Download the Ubuntu image <http://www.ubuntu.com/download/ubuntu/download>`_ - make sure you choose Ubuntu 10.04.
Install VirtualBox
******************
@@ -59,19 +59,16 @@
Go to Applications and open VirtualBox, then click New:
.. image:: images/virtualbox4-newvm.png
- :width: 807px
:alt: The VirtualBox installer - the New Virtual Machine Wizard
-Give your VM a name - we'll call ours ``ubuntu_ckan``. Under **OS Type**, choose **Linux** and **Ubuntu 64-bit**.
+Give your VM a name - we'll call ours ``ubuntu_ckan``. Under **OS Type**, choose **Linux** and **Ubuntu (32 or 64-bit)**.
.. image:: images/virtualbox5-vmtype.png
- :width: 807px
:alt: The VirtualBox installer - choosing your operating system
Leave the memory size as 512MB, and choose **Create new hard disk**. This will open a new wizard:
.. image:: images/virtualbox6-vmloc.png
- :width: 807px
:alt: The VirtualBox installer - creating a new hard disk
You can leave the defaults unchanged here too - click **Continue**, and then **Done**, and **Done** again, to create a new VM.
@@ -79,19 +76,16 @@
Next, choose your VM from the left-hand menu, and click **Start**:
.. image:: images/virtualbox7-startvm.png
- :width: 807px
:alt: Starting your new VM
This will open the First Run Wizard:
.. image:: images/virtualbox8-firstrun.png
- :width: 807px
:alt: The VirtualBox First Run Wizard
After clicking **Continue**, you'll see **Select Installation Media**. This is where we need to tell our VM to boot from Ubuntu. Click on the file icon, and find your Ubuntu ``.iso`` file:
.. image:: images/virtualbox9-iso.png
- :width: 807px
:alt: When you get to Select Installation Media, choose your Ubuntu .iso file
Click **Done**, wait for a few seconds, and you will see your Ubuntu VM booting.
--- a/doc/install-from-source.rst Mon Aug 08 15:48:28 2011 +0100
+++ b/doc/install-from-source.rst Mon Aug 08 15:56:56 2011 +0100
@@ -4,7 +4,7 @@
This section describes how to install CKAN from source. This removes the requirement for Ubuntu 10.04 that exists with :doc:`install-from-package`.
-.. warning:: This option is more complex than :doc:`install-from-package`, so we suggest only doing it this way if you plan to work on CKAN core, or have no access to Ubuntu 10.04 via any of the suggested methods.
+.. warning:: This option is more complex than :doc:`install-from-package`, so f you prefer simplicity we suggest installing from package.
For support during installation, please contact `the ckan-dev mailing list <http://lists.okfn.org/mailman/listinfo/ckan-dev>`_.
--- a/doc/paster.rst Mon Aug 08 15:48:28 2011 +0100
+++ b/doc/paster.rst Mon Aug 08 15:56:56 2011 +0100
@@ -99,16 +99,38 @@
db: Manage databases
--------------------
-Lets you initialise, upgrade, and dump database files in various formats.
+Lets you initialise, upgrade, and dump the CKAN database.
-For example, to initialise the CKAN database, creating the tables that CKAN uses (note that you don't need to do this during setup if you have run ``create-test-data``)::
+Initialisation
+~~~~~~~~~~~~~~
+
+Before you can run CKAN for the first time, you need to run "db init" to create the tables in the database and the default authorization settings::
paster --plugin=ckan db init --config=/etc/ckan/std/std.ini
-When you upgrade CKAN software by any method *other* than the package update described in :doc:`upgrade`, before you restart it, you should run 'db upgrade', to migrate the database tables if necessary::
+If you forget to do this then CKAN won't serve requests and you will see errors such as this in the logs::
+
+ ProgrammingError: (ProgrammingError) relation "user" does not exist
+
+Cleaning
+~~~~~~~~
+
+You can delete everything in the CKAN database, including the tables, to start from scratch::
+
+ paster --plugin=ckan db clean --config=/etc/ckan/std/std.ini
+
+The next logical step from this point is to do a "db init" step before starting CKAN again.
+
+Upgrade migration
+~~~~~~~~~~~~~~~~~
+
+When you upgrade CKAN software by any method *other* than the package update described in :doc:`upgrade`, before you restart it, you should run 'db upgrade', which will do any necessary migrations to the database tables::
paster --plugin=ckan db upgrade --config=/etc/ckan/std/std.ini
+Creating dump files
+~~~~~~~~~~~~~~~~~~~
+
For information on using ``db`` to create dumpfiles, see :doc:`database_dumps`.
http://bitbucket.org/okfn/ckan/changeset/69396d5d1eae/
changeset: 69396d5d1eae
branch: feature-1253-authz-refactor
user: amercader
date: 2011-08-08 19:23:29
summary: [auth,tests] Update auth test for package listing
affected #: 1 file (226 bytes)
--- a/ckan/tests/functional/test_authz.py Mon Aug 08 15:56:56 2011 +0100
+++ b/ckan/tests/functional/test_authz.py Mon Aug 08 18:23:29 2011 +0100
@@ -351,11 +351,8 @@
def test_list(self):
# NB there is no listing of package in wui interface any more
- self._test_can('list', [self.testsysadmin, self.pkggroupadmin], ['xx', 'rx', 'wx', 'rr', 'wr', 'ww'], interfaces=['rest'])
- self._test_can('list', self.mrloggedin, ['rx', 'wx', 'rr', 'wr', 'ww'], interfaces=['rest'])
- self._test_can('list', self.visitor, ['rr', 'wr', 'ww'], interfaces=['rest'])
- self._test_cant('list', self.mrloggedin, ['xx'], interfaces=['rest'])
- self._test_cant('list', self.visitor, ['xx', 'rx', 'wx'], interfaces=['rest'])
+ # NB under the new model all active packages are always visible in listings by default
+ self._test_can('list', [self.testsysadmin, self.pkggroupadmin, self.mrloggedin, self.visitor], ['xx', 'rx', 'wx', 'rr', 'wr', 'ww'], interfaces=['rest'])
def test_admin_edit_deleted(self):
self._test_can('edit', self.pkggroupadmin, ['xx', 'rx', 'wx', 'rr', 'wr', 'ww', 'deleted'])
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