From 277bb7672f3cf5de7be578893c8219d0a3d294e3 Mon Sep 17 00:00:00 2001 From: Ross Jones Date: Fri, 24 Feb 2012 11:20:49 +0000 Subject: [PATCH] Fixing problem with publisher_form and moving setup_template_variables out of plugin --- ckan/controllers/group.py | 2 +- ckan/lib/alphabet_paginate.py | 8 ++- ckan/lib/authenticator.py | 8 +-- ckan/lib/dictization/__init__.py | 8 +-- ckan/lib/dictization/model_dictize.py | 76 +++++++++++++-------------- ckan/logic/action/get.py | 2 +- ckan/logic/schema.py | 6 ++- ckanext/publisher_form/forms.py | 72 ++++++++++++++++--------- 8 files changed, 104 insertions(+), 78 deletions(-) diff --git a/ckan/controllers/group.py b/ckan/controllers/group.py index 5f16e2ae6b4..6e5700cbdb7 100644 --- a/ckan/controllers/group.py +++ b/ckan/controllers/group.py @@ -176,7 +176,7 @@ def _form_to_db_schema(self, group_type=None): def _db_to_form_schema(self, group_type=None): '''This is an interface to manipulate data from the database into a format suitable for the form (optional)''' - return _lookup_plugin(group_type).form_to_db_schema() + return _lookup_plugin(group_type).db_to_form_schema() def _setup_template_variables(self, context, data_dict, group_type=None): return _lookup_plugin(group_type).setup_template_variables(context,data_dict) diff --git a/ckan/lib/alphabet_paginate.py b/ckan/lib/alphabet_paginate.py index 4277867463f..5777d5c87bf 100644 --- a/ckan/lib/alphabet_paginate.py +++ b/ckan/lib/alphabet_paginate.py @@ -45,11 +45,15 @@ def __init__(self, collection, alpha_attribute, page, other_text, paging_thresho self.controller_name = controller_name self.available = dict( (c,0,) for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) for c in self.collection: - x = c if isinstance( c, unicode ) else getattr(c, self.alpha_attribute)[0] + if isinstance(c, unicode): + x = c + elif isinstance(c, dict): + x = c[self.alpha_attribute][0] + else: + x = getattr(c, self.alpha_attribute)[0] self.available[x] = self.available.get(x, 0) + 1 - def pager(self, q=None): '''Returns pager html - for navigating between the pages. e.g. Something like this: diff --git a/ckan/lib/authenticator.py b/ckan/lib/authenticator.py index 8eea54ae347..b56711a3427 100644 --- a/ckan/lib/authenticator.py +++ b/ckan/lib/authenticator.py @@ -5,7 +5,7 @@ class OpenIDAuthenticator(object): implements(IAuthenticator) - + def authenticate(self, environ, identity): if 'repoze.who.plugins.openid.userid' in identity: openid = identity.get('repoze.who.plugins.openid.userid') @@ -15,16 +15,16 @@ def authenticate(self, environ, identity): else: return user.name return None - + class UsernamePasswordAuthenticator(object): implements(IAuthenticator) - + def authenticate(self, environ, identity): if not 'login' in identity or not 'password' in identity: return None user = User.by_name(identity.get('login')) - if user is None: + if user is None: return None if user.validate_password(identity.get('password')): return user.name diff --git a/ckan/lib/dictization/__init__.py b/ckan/lib/dictization/__init__.py index 66d4abf6dec..42f748b07d6 100644 --- a/ckan/lib/dictization/__init__.py +++ b/ckan/lib/dictization/__init__.py @@ -3,7 +3,7 @@ import sqlalchemy from pylons import config -# NOTE +# NOTE # The functions in this file contain very generic methods for dictizing objects # and saving dictized objects. If a specialised use is needed please do NOT extend # these functions. Copy code from here as needed. @@ -68,7 +68,7 @@ def obj_list_dictize(obj_list, context, sort_key=lambda x:x): return sorted(result_list, key=sort_key) def obj_dict_dictize(obj_dict, context, sort_key=lambda x:x): - '''Get a dict whose values are model objects + '''Get a dict whose values are model objects and represent it as a list of dicts''' result_list = [] @@ -93,7 +93,7 @@ def get_unique_constraints(table, context): def table_dict_save(table_dict, ModelClass, context): '''Given a dict and a model class, update or create a sqlalchemy object. - This will use an existing object if "id" is supplied OR if any unique + This will use an existing object if "id" is supplied OR if any unique constraints are met. e.g supplying just a tag name will get out that tag obj. ''' @@ -107,7 +107,7 @@ def table_dict_save(table_dict, ModelClass, context): unique_constriants = get_unique_constraints(table, context) id = table_dict.get("id") - + if id: obj = session.query(ModelClass).get(id) diff --git a/ckan/lib/dictization/model_dictize.py b/ckan/lib/dictization/model_dictize.py index 2d60cc6a705..e5d51259eb0 100644 --- a/ckan/lib/dictization/model_dictize.py +++ b/ckan/lib/dictization/model_dictize.py @@ -12,7 +12,7 @@ ## package save -def group_list_dictize(obj_list, context, +def group_list_dictize(obj_list, context, sort_key=lambda x:x['display_name'], reverse=False): active = context.get('active', True) @@ -93,10 +93,10 @@ def _execute_with_revision(q, rev_table, context): But you can provide revision_id, revision_date or pending in the context and it will filter to an earlier time or the latest unmoderated object revision. - + Raises NotFound if context['revision_id'] is provided, but the revision ID does not exist. - + Returns [] if there are no results. ''' @@ -113,7 +113,7 @@ def _execute_with_revision(q, rev_table, context): if not revision: raise NotFound revision_date = revision.timestamp - + if revision_date: q = q.where(rev_table.c.revision_timestamp <= revision_date) q = q.where(rev_table.c.expired_timestamp > revision_date) @@ -133,7 +133,7 @@ def package_dictize(pkg, context): but you can provide revision_id, revision_date or pending in the context and it will filter to an earlier time or the latest unmoderated object revision. - + May raise NotFound. TODO: understand what the specific set of circumstances are that cause this. ''' @@ -148,7 +148,7 @@ def package_dictize(pkg, context): #resources res_rev = model.resource_revision_table resource_group = model.resource_group_table - q = select([res_rev], from_obj = res_rev.join(resource_group, + q = select([res_rev], from_obj = res_rev.join(resource_group, resource_group.c.id == res_rev.c.resource_group_id)) q = q.where(resource_group.c.package_id == pkg.id) result = _execute_with_revision(q, res_rev, context) @@ -156,7 +156,7 @@ def package_dictize(pkg, context): #tags tag_rev = model.package_tag_revision_table tag = model.tag_table - q = select([tag, tag_rev.c.state, tag_rev.c.revision_timestamp], + q = select([tag, tag_rev.c.state, tag_rev.c.revision_timestamp], from_obj=tag_rev.join(tag, tag.c.id == tag_rev.c.tag_id) ).where(tag_rev.c.package_id == pkg.id) result = _execute_with_revision(q, tag_rev, context) @@ -183,7 +183,7 @@ def package_dictize(pkg, context): q = select([rel_rev]).where(rel_rev.c.object_package_id == pkg.id) result = _execute_with_revision(q, rel_rev, context) result_dict["relationships_as_object"] = obj_list_dictize(result, context) - + # Extra properties from the domain object # We need an actual Package object for this, not a PackageRevision if isinstance(pkg,PackageRevision): @@ -264,10 +264,10 @@ def tag_dictize(tag, context): result_dict["packages"] = obj_list_dictize( tag.packages_ordered, context) - - return result_dict -def user_list_dictize(obj_list, context, + return result_dict + +def user_list_dictize(obj_list, context, sort_key=lambda x:x['name'], reverse=False): result_list = [] @@ -288,13 +288,13 @@ def user_dictize(user, context): result_dict = table_dictize(user, context) del result_dict['password'] - + result_dict['display_name'] = user.display_name result_dict['email_hash'] = user.email_hash result_dict['number_of_edits'] = user.number_of_edits() result_dict['number_administered_packages'] = user.number_administered_packages() - return result_dict + return result_dict def task_status_dictize(task_status, context): return table_dictize(task_status, context) @@ -302,23 +302,23 @@ def task_status_dictize(task_status, context): ## conversion to api def group_to_api1(group, context): - + dictized = group_dictize(group, context) - dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) + dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) for extra in dictized["extras"]) dictized["packages"] = sorted([package["name"] for package in dictized["packages"]]) return dictized def group_to_api2(group, context): - + dictized = group_dictize(group, context) - dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) + dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) for extra in dictized["extras"]) dictized["packages"] = sorted([package["id"] for package in dictized["packages"]]) return dictized def tag_to_api1(tag, context): - + dictized = tag_dictize(tag, context) return sorted([package["name"] for package in dictized["packages"]]) @@ -342,18 +342,18 @@ def package_to_api1(pkg, context): dictized["groups"] = [group["name"] for group in dictized["groups"]] dictized["tags"] = [tag["name"] for tag in dictized["tags"]] - dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) + dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) for extra in dictized["extras"]) dictized['notes_rendered'] = ckan.misc.MarkdownFormat().to_html(pkg.notes) - resources = dictized["resources"] - + resources = dictized["resources"] + for resource in resources: resource_dict_to_api(resource, pkg.id, context) if pkg.resources: dictized['download_url'] = pkg.resources[0].url - + dictized['license'] = pkg.license.title if pkg.license else None dictized['ratings_average'] = pkg.get_average_rating() @@ -368,9 +368,9 @@ def package_to_api1(pkg, context): dictized['metadata_created'] = metadata_created.isoformat() \ if metadata_created else None - subjects = dictized.pop("relationships_as_subject") - objects = dictized.pop("relationships_as_object") - + subjects = dictized.pop("relationships_as_subject") + objects = dictized.pop("relationships_as_object") + relationships = [] for relationship in objects: model = context['model'] @@ -386,9 +386,9 @@ def package_to_api1(pkg, context): 'type': relationship['type'], 'object': pkg.get(relationship['object_package_id']).name, 'comment': relationship["comment"]}) - - - dictized['relationships'] = relationships + + + dictized['relationships'] = relationships return dictized def package_to_api2(pkg, context): @@ -397,16 +397,16 @@ def package_to_api2(pkg, context): dictized["groups"] = [group["id"] for group in dictized["groups"]] dictized.pop("revision_timestamp") - + dictized["tags"] = [tag["name"] for tag in dictized["tags"]] - dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) + dictized["extras"] = dict((extra["key"], json.loads(extra["value"])) for extra in dictized["extras"]) - resources = dictized["resources"] - + resources = dictized["resources"] + for resource in resources: resource_dict_to_api(resource,pkg.id, context) - + dictized['license'] = pkg.license.title if pkg.license else None dictized['ratings_average'] = pkg.get_average_rating() @@ -420,9 +420,9 @@ def package_to_api2(pkg, context): if pkg.metadata_created else None dictized['notes_rendered'] = ckan.misc.MarkdownFormat().to_html(pkg.notes) - subjects = dictized.pop("relationships_as_subject") - objects = dictized.pop("relationships_as_object") - + subjects = dictized.pop("relationships_as_subject") + objects = dictized.pop("relationships_as_object") + relationships = [] for relationship in objects: model = context['model'] @@ -438,8 +438,8 @@ def package_to_api2(pkg, context): 'type': relationship['type'], 'object': relationship['object_package_id'], 'comment': relationship["comment"]}) - - dictized['relationships'] = relationships + + dictized['relationships'] = relationships return dictized def activity_dictize(activity, context): diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index 8ff532b746b..42a4aff6696 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -882,7 +882,7 @@ def get_site_user(context, data_dict): def roles_show(context, data_dict): '''Returns the roles that users (and authorization groups) have on a particular domain_object. - + If you specify a user (or authorization group) then the resulting roles will be filtered by those of that user (or authorization group). diff --git a/ckan/logic/schema.py b/ckan/logic/schema.py index aa2610309bb..5455ad11713 100644 --- a/ckan/logic/schema.py +++ b/ckan/logic/schema.py @@ -170,6 +170,8 @@ def default_group_schema(): '__extras': [ignore], 'packages': { "id": [not_empty, unicode, package_id_or_name_exists], + "name": [not_empty, unicode], + "title":[not_empty, unicode], "__extras": [ignore] } } @@ -184,9 +186,9 @@ def group_form_schema(): } schema['users'] = { "name": [not_empty, unicode], - "capacity": [ignore_missing], + "capacity": [ignore_missing], "__extras": [ignore] - } + } return schema diff --git a/ckanext/publisher_form/forms.py b/ckanext/publisher_form/forms.py index 216d9f6f2ca..a1a4f72d8b6 100644 --- a/ckanext/publisher_form/forms.py +++ b/ckanext/publisher_form/forms.py @@ -19,6 +19,7 @@ from ckan.lib.navl.dictization_functions import DataError, flatten_dict, unflatten from ckan.plugins import IDatasetForm, IGroupForm, IConfigurer from ckan.plugins import implements, SingletonPlugin +from ckan.logic import check_access from ckan.lib.navl.validators import (ignore_missing, not_empty, @@ -31,13 +32,13 @@ class PublisherForm(SingletonPlugin): """ - This plugin implements an IGroupForm for form associated with a + This plugin implements an IGroupForm for form associated with a publisher group. ``IConfigurer`` is used to add the local template path and the IGroupForm supplies the custom form. """ implements(IGroupForm, inherit=True) - implements(IConfigurer, inherit=True) - + implements(IConfigurer, inherit=True) + def update_config(self, config): """ This IConfigurer implementation causes CKAN to look in the @@ -49,12 +50,12 @@ def update_config(self, config): 'publisher_form', 'templates') config['extra_template_paths'] = ','.join([template_dir, config.get('extra_template_paths', '')]) - + def group_form(self): """ Returns a string representing the location of the template to be rendered. e.g. "forms/group_form.html". - """ + """ return 'publisher_form.html' def group_types(self): @@ -75,11 +76,11 @@ def is_fallback(self): Returns true iff this provides the fallback behaviour, when no other plugin instance matches a group's type. - As this is not the fallback controller we should return False. If + As this is not the fallback controller we should return False. If we were wanting to act as the fallback, we'd return True """ - return False - + return False + def form_to_db_schema(self): """ Returns the schema for mapping group data from a form to a format @@ -93,7 +94,7 @@ def db_to_form_schema(self): format suitable for the form (optional) """ return {} - + def check_data_dict(self, data_dict): """ Check if the return data is correct. @@ -104,22 +105,42 @@ def check_data_dict(self, data_dict): def setup_template_variables(self, context, data_dict): """ Add variables to c just prior to the template being rendered. We should - use the available groups for the current user, but should be optional + use the available groups for the current user, but should be optional in case this is a top level group - """ - #c.user_groups = c.userobj.get_groups('publisher') - c.user_groups = ['One', 'Two', 'Three'] - - + """ + c.is_sysadmin = Authorizer().is_sysadmin(c.user) + if 'group' in context: + group = context['group'] + + try: + check_access('group_update', context) + c.is_superuser_or_groupadmin = True + except NotAuthorized: + c.is_superuser_or_groupadmin = False + + c.possible_parents = model.Session.query(model.Group).\ + filter(model.Group.state == 'active').\ + filter(model.Group.type == 'publisher').\ + filter(model.Group.name != group.id ).order_by(model.Group.title).all() + + c.parent = None + grps = group.get_groups('publisher') + if grps: + c.parent = grps[0] + + c.users = group.members_of_type(model.User) + + + class PublisherDatasetForm(SingletonPlugin): """ - This plugin implements a new publisher form for cases where we + This plugin implements a new publisher form for cases where we want to enforce group (type=publisher) membership on a dataset. """ implements(IDatasetForm, inherit=True) - implements(IConfigurer, inherit=True) - + implements(IConfigurer, inherit=True) + def update_config(self, config): """ This IConfigurer implementation causes CKAN to look in the @@ -131,12 +152,12 @@ def update_config(self, config): 'publisher_form', 'templates') config['extra_template_paths'] = ','.join([template_dir, config.get('extra_template_paths', '')]) - + def package_form(self): """ Returns a string representing the location of the template to be rendered. e.g. "package/new_package_form.html". - """ + """ return 'dataset_form.html' def is_fallback(self): @@ -144,7 +165,7 @@ def is_fallback(self): Returns true iff this provides the fallback behaviour, when no other plugin instance matches a package's type. - As this is not the fallback controller we should return False. If + As this is not the fallback controller we should return False. If we were wanting to act as the fallback, we'd return True """ return True @@ -166,14 +187,14 @@ def setup_template_variables(self, context, data_dict=None): """ Adds variables to c just prior to the template being rendered that can then be used within the form - """ + """ c.licences = [('', '')] + model.Package.get_license_options() c.publishers = [('Example publisher', 'Example publisher 2')] c.is_sysadmin = Authorizer().is_sysadmin(c.user) c.resource_columns = model.Resource.get_columns() c.groups_available = c.userobj.get_groups('publisher') if c.userobj else [] - - + + ## This is messy as auths take domain object not data_dict pkg = context.get('package') or c.pkg if pkg: @@ -186,7 +207,7 @@ def form_to_db_schema(self): suitable for the database. """ return package_form_schema() - + def db_to_form_schema(data): """ Returns the schema for mapping package data from the database into a @@ -200,4 +221,3 @@ def check_data_dict(self, data_dict): """ pass - \ No newline at end of file