Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/okfn/ckan
Browse files Browse the repository at this point in the history
  • Loading branch information
rossjones committed May 8, 2012
2 parents d646258 + 5c1dc6f commit 20d06d6
Show file tree
Hide file tree
Showing 19 changed files with 702 additions and 571 deletions.
26 changes: 18 additions & 8 deletions ckan/authz.py
Expand Up @@ -154,13 +154,23 @@ def get_roles(cls, username, domain_obj):
return [pr.role for pr in q]

@classmethod
def is_sysadmin(cls, username):
user = model.User.by_name(username, autoflush=False)
if user:
q = model.Session.query(model.SystemRole)
q = q.autoflush(False)
q = q.filter_by(role=model.Role.ADMIN, user=user)
return q.count() > 0
def is_sysadmin(cls, user):
'''Returns whether the given user a sys-admin?
(sysadmin = system administrator with full authorization)
Ideally provide a user object. Next best is a user name.
'''
if not user:
return False
if isinstance(user, basestring):
user = model.User.by_name(user, autoflush=False)
if not user:
return False
elif not isinstance(user, model.User):
raise NotImplementedError
q = model.Session.query(model.SystemRole)
q = q.autoflush(False)
q = q.filter_by(role=model.Role.ADMIN, user=user)
return q.count() > 0

@classmethod
def get_admins(cls, domain_obj):
Expand Down Expand Up @@ -188,7 +198,7 @@ def authorized_query(cls, username, entity, action=model.Action.READ):
visitor = model.User.by_name(model.PSEUDO_USER__VISITOR, autoflush=False)
logged_in = model.User.by_name(model.PSEUDO_USER__LOGGED_IN,
autoflush=False)
if not cls.is_sysadmin(username):
if not cls.is_sysadmin(user):
# This gets the role table the entity is joined to. we
# need to use this in the queries below as if we use
# model.UserObjectRole a cross join happens always
Expand Down
2 changes: 2 additions & 0 deletions ckan/controllers/admin.py
Expand Up @@ -234,6 +234,8 @@ def action_add_form(users_or_authz_groups):
c.authz_groups = authz_groups
c.authz_groups_role_dict = authz_groups_role_dict

c.are_any_authz_groups = bool(model.Session.query(model.AuthorizationGroup).count())

return render('admin/authz.html')

def trash(self):
Expand Down
2 changes: 1 addition & 1 deletion ckan/controllers/api.py
Expand Up @@ -591,7 +591,7 @@ def group_autocomplete(self):
limit = 20
limit = min(50, limit)

query = model.Group.search_by_name(q, t)
query = model.Group.search_by_name_or_title(q, t)
def convert_to_dict(user):
out = {}
for k in ['id', 'name', 'title']:
Expand Down
2 changes: 1 addition & 1 deletion ckan/lib/base.py
Expand Up @@ -541,7 +541,7 @@ def _prepare_authz_info_for_render(self, user_object_roles):
c.user_role_dict = user_role_dict
c.authz_groups = authz_groups
c.authz_groups_role_dict = authz_groups_role_dict

c.are_any_authz_groups = bool(model.Session.query(model.AuthorizationGroup).count())

# Include the '_' function in the public names
__all__ = [__name for __name in locals().keys() if not __name.startswith('_') \
Expand Down
62 changes: 62 additions & 0 deletions ckan/lib/create_test_data.py
Expand Up @@ -628,6 +628,68 @@ def reset(cls):
def get_all_data(cls):
return cls.pkg_names + list(cls.group_names) + cls.tag_names + cls.user_refs

@classmethod
def make_some_vocab_tags(cls):
import ckan.model as model
model.repo.new_revision()

# Create a couple of vocabularies.
genre_vocab = model.Vocabulary(u'genre')
model.Session.add(genre_vocab)
composers_vocab = model.Vocabulary(u'composers')
model.Session.add(composers_vocab)

# Create some additional free tags for tag search tests.
tolkien_tag = model.Tag(name="tolkien")
model.Session.add(tolkien_tag)
toledo_tag = model.Tag(name="toledo")
model.Session.add(toledo_tag)
tolerance_tag = model.Tag(name="tolerance")
model.Session.add(tolerance_tag)
tollbooth_tag = model.Tag(name="tollbooth")
model.Session.add(tollbooth_tag)
# We have to add free tags to a package or they won't show up in tag results.
model.Package.get('warandpeace').add_tags((tolkien_tag, toledo_tag,
tolerance_tag, tollbooth_tag))

# Create some tags that belong to vocabularies.
sonata_tag = model.Tag(name=u'sonata', vocabulary_id=genre_vocab.id)
model.Session.add(sonata_tag)

bach_tag = model.Tag(name=u'Bach', vocabulary_id=composers_vocab.id)
model.Session.add(bach_tag)

neoclassical_tag = model.Tag(name='neoclassical',
vocabulary_id=genre_vocab.id)
model.Session.add(neoclassical_tag)

neofolk_tag = model.Tag(name='neofolk', vocabulary_id=genre_vocab.id)
model.Session.add(neofolk_tag)

neomedieval_tag = model.Tag(name='neomedieval',
vocabulary_id=genre_vocab.id)
model.Session.add(neomedieval_tag)

neoprog_tag = model.Tag(name='neoprog',
vocabulary_id=genre_vocab.id)
model.Session.add(neoprog_tag)

neopsychedelia_tag = model.Tag(name='neopsychedelia',
vocabulary_id=genre_vocab.id)
model.Session.add(neopsychedelia_tag)

neosoul_tag = model.Tag(name='neosoul', vocabulary_id=genre_vocab.id)
model.Session.add(neosoul_tag)

nerdcore_tag = model.Tag(name='nerdcore', vocabulary_id=genre_vocab.id)
model.Session.add(nerdcore_tag)

model.Package.get('warandpeace').add_tag(bach_tag)
model.Package.get('annakarenina').add_tag(sonata_tag)

model.Session.commit()



search_items = [{'name':'gils',
'title':'Government Information Locator Service',
Expand Down
17 changes: 10 additions & 7 deletions ckan/logic/action/get.py
Expand Up @@ -321,7 +321,7 @@ def tag_list(context, data_dict):
check_access('tag_list', context, data_dict)

if query:
tags = _tag_search(context, data_dict)
tags, count = _tag_search(context, data_dict)
else:
tags = model.Tag.all(vocab_id_or_name)

Expand Down Expand Up @@ -921,7 +921,8 @@ def resource_search(context, data_dict):
return {'count': count, 'results': results}

def _tag_search(context, data_dict):
'''Return a list of tag objects that contain the given string.
'''Return a list of tag objects that contain the given string and
the full count (for paging).
The query string should be provided in the data_dict with key 'query' or
'q'.
Expand All @@ -930,6 +931,7 @@ def _tag_search(context, data_dict):
searched. If a 'vocabulary_id' is provided in the data_dict then tags
belonging to the given vocabulary (id or name) will be searched instead.
Use 'offset' and 'limit' parameters to page through results.
'''
model = context['model']

Expand Down Expand Up @@ -963,15 +965,16 @@ def _tag_search(context, data_dict):
terms.append(value)

if not len(terms):
return []
return [], 0

for term in terms:
escaped_term = misc.escape_sql_like_special_characters(term, escape='\\')
q = q.filter(model.Tag.name.ilike('%' + escaped_term + '%'))

count = q.count()
q = q.offset(offset)
q = q.limit(limit)
return q.all()
return q.all(), count

def tag_search(context, data_dict):
'''Return a list of tag dictionaries that contain the given string.
Expand All @@ -987,8 +990,8 @@ def tag_search(context, data_dict):
and 'results' (the list of tag dicts).
'''
tags = _tag_search(context, data_dict)
return {'count': len(tags),
tags, count = _tag_search(context, data_dict)
return {'count': count,
'results': [table_dictize(tag, context) for tag in tags]}

def tag_autocomplete(context, data_dict):
Expand All @@ -1003,7 +1006,7 @@ def tag_autocomplete(context, data_dict):
'''
check_access('tag_autocomplete', context, data_dict)
matching_tags = _tag_search(context, data_dict)
matching_tags, count = _tag_search(context, data_dict)
if matching_tags:
return [tag.name for tag in matching_tags]
else:
Expand Down
12 changes: 7 additions & 5 deletions ckan/model/group.py
Expand Up @@ -9,6 +9,7 @@
import vdm.sqlalchemy
from ckan.model import extension, User
from sqlalchemy.ext.associationproxy import association_proxy
import sqlalchemy as sa

__all__ = ['group_table', 'Group', 'package_revision_table',
'Member', 'GroupRevision', 'MemberRevision',
Expand Down Expand Up @@ -181,12 +182,13 @@ def active_packages(self, load_eager=True, with_private=False):
return query

@classmethod
def search_by_name(cls, text_query, group_type=None):
def search_by_name_or_title(cls, text_query, group_type=None):
text_query = text_query.strip().lower()
if not group_type:
q = Session.query(cls).filter(cls.name.contains(text_query))
else:
q = Session.query(cls).filter(cls.name.contains(text_query)).filter(cls.type==group_type)
q = Session.query(cls) \
.filter(sa.or_(cls.name.contains(text_query),
cls.title.ilike('%' + text_query + '%')))
if group_type:
q = q.filter(cls.type==group_type)
return q.order_by(cls.title)

def as_dict(self, ref_package_by='name'):
Expand Down
8 changes: 4 additions & 4 deletions ckan/plugins/toolkit.py
Expand Up @@ -16,15 +16,15 @@ class CkanVersionException(Exception):

class _Toolkit(object):
'''This class is intended to make functions/objects consistently
available to plugins, whilst giving developers the ability move
available to plugins, whilst giving core CKAN developers the ability move
code around or change underlying frameworks etc. This object allows
us to avoid circular imports while making functions/objects
available to plugins.
It should not be used internally within ckan only by extensions.
It should not be used internally within ckan - only by extensions.
Functions/objects should only be removed after reasonable
depreciation notice has been given.'''
deprecation notice has been given.'''

# contents should describe the available functions/objects. We check
# that this list matches the actual availables in the initialisation
Expand Down Expand Up @@ -175,7 +175,7 @@ def _requires_ckan_version(cls, min_version, max_version=None):
if not max_version:
error = 'Requires ckan version %s or higher' % min_version
else:
error = 'Requires ckan version between %s and %s' % \
error = 'Requires ckan version between %s and %s' % \
(min_version, max_version)
raise cls.CkanVersionException(error)

Expand Down
3 changes: 2 additions & 1 deletion ckan/templates/admin/authz.html
Expand Up @@ -26,6 +26,7 @@ <h3>Add Roles for Any User</h3>

<hr/>

<py:if test="c.are_any_authz_groups">
<h3>Existing Roles for Authorization Groups</h3>

<form id="authzgroup_form" method="POST">
Expand All @@ -41,7 +42,7 @@ <h3>Add Roles for Any Authorization Group</h3>
<button type="submit" name="authz_add" class="btn btn-primary">Add Role</button>
<div class="clear"></div>
</form>

</py:if>



Expand Down
2 changes: 2 additions & 0 deletions ckan/templates/group/authz.html
Expand Up @@ -25,6 +25,7 @@ <h3>Add Roles for Any User</h3>

<hr/>

<py:if test="c.are_any_authz_groups">
<h3>Update Existing Roles for Authorization Groups</h3>

<form id="authzgroup_form" method="POST">
Expand All @@ -40,6 +41,7 @@ <h3>Add Roles for Any Authorization Group</h3>
<button type="submit" name="authz_add" class="btn btn-primary">Add Role</button>
<div class="clear"></div>
</form>
</py:if>

</div>

Expand Down
2 changes: 2 additions & 0 deletions ckan/templates/package/authz.html
Expand Up @@ -25,6 +25,7 @@ <h3>Add Roles for Any User</h3>

<hr/>

<py:if test="c.are_any_authz_groups">
<h3>Update Existing Roles for Authorization Groups</h3>

<form id="authzgroup_form" method="POST">
Expand All @@ -40,6 +41,7 @@ <h3>Add Roles for Any Authorization Group</h3>
<button type="submit" name="authz_add" class="btn btn-primary">Add Role</button>
<div class="clear"></div>
</form>
</py:if>

</div>

Expand Down
9 changes: 9 additions & 0 deletions ckan/tests/__init__.py
Expand Up @@ -349,3 +349,12 @@ def assert_in(a, b):
assert a in b, '%r was not in %r' % (a, b)
def assert_not_in(a, b):
assert a not in b, '%r was in %r' % (a, b)

class StatusCodes:
STATUS_200_OK = 200
STATUS_201_CREATED = 201
STATUS_400_BAD_REQUEST = 400
STATUS_403_ACCESS_DENIED = 403
STATUS_404_NOT_FOUND = 404
STATUS_409_CONFLICT = 409

16 changes: 16 additions & 0 deletions ckan/tests/functional/api/test_util.py
Expand Up @@ -100,6 +100,22 @@ def test_tag_autocomplete(self):
assert_equal(response.body, '{"ResultSet": {"Result": [{"Name": "russian"}]}}')
assert_equal(response.header('Content-Type'), 'application/json;charset=utf-8')

def test_group_autocomplete(self):
url = url_for(controller='api', action='group_autocomplete', ver=2)
assert_equal(url, '/api/2/util/group/autocomplete')
response = self.app.get(
url=url,
params={
'q': u'dave',
},
status=200,
)
results = json.loads(response.body)
assert_equal(len(results), 1)
assert_equal(results[0]['name'], 'david')
assert_equal(results[0]['title'], 'Dave\'s books')
assert_equal(response.header('Content-Type'), 'application/json;charset=utf-8')

def test_markdown(self):
markdown = '''##Title'''
response = self.app.get(
Expand Down

0 comments on commit 20d06d6

Please sign in to comment.