Skip to content

Commit

Permalink
Merge branch 'master' of github.com:okfn/ckan into 1601-defect-organi…
Browse files Browse the repository at this point in the history
…zations-server-error
  • Loading branch information
Sean Hammond committed Jul 2, 2012
2 parents bfb48c0 + 2df478d commit 3676cfe
Show file tree
Hide file tree
Showing 56 changed files with 2,838 additions and 618 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.txt
@@ -1,6 +1,26 @@
CKAN CHANGELOG
++++++++++++++

v1.8
====

* [#2592,#2428] Ubuntu 12.04 Precise is now supported with CKAN source install.
The source install instructions have been updated and simplified.
Some of CKAN's dependencies have been updated and some removed.
* Requirements have been updated see doc/install-from-source.rst
users will need to do a new pip install (#2592)
* [#2304,#2305] New 'follow' feature. You'll now see a 'Followers' tab on user
and dataset pages, where you can see how many users are following that user
or dataset. If you're logged in, you'll see a 'Follow' button on the pages
of datasets and other users that you can click to follow them. Also when
logged in, if you go to your own user page you'll see a new 'Dashboard' tab
where you can see an activity stream from of all the users and datasets that
you're following. There are also API calls for the follow features, see the
Action API reference documentation.
* [#2345] New action API reference docs. The documentation for CKAN's Action
API has been rewritten, with each function and its arguments and return
values now individually documented.

v1.7.1 2012-06-20
=================

Expand Down
4 changes: 2 additions & 2 deletions ckan/config/middleware.py
Expand Up @@ -20,7 +20,7 @@

from ckan.plugins import PluginImplementations
from ckan.plugins.interfaces import IMiddleware
from ckan.lib.i18n import get_locales
from ckan.lib.i18n import get_locales_from_config

from ckan.config.environment import load_environment

Expand Down Expand Up @@ -145,7 +145,7 @@ class I18nMiddleware(object):
def __init__(self, app, config):
self.app = app
self.default_locale = config.get('ckan.locale_default', 'en')
self.local_list = get_locales()
self.local_list = get_locales_from_config()

def __call__(self, environ, start_response):
# strip the language selector from the requested url
Expand Down
1 change: 1 addition & 0 deletions ckan/config/routing.py
Expand Up @@ -252,6 +252,7 @@ def make_map():
m.connect('/user/edit', action='edit')
# Note: openid users have slashes in their ids, so need the wildcard
# in the route.
m.connect('/user/dashboard', action='dashboard')
m.connect('/user/followers/{id:.*}', action='followers')
m.connect('/user/edit/{id:.*}', action='edit')
m.connect('/user/reset/{id:.*}', action='perform_reset')
Expand Down
2 changes: 2 additions & 0 deletions ckan/config/solr/CHANGELOG.txt
Expand Up @@ -8,6 +8,8 @@ v1.4 - (ckan>=1.7)
* Add title_string so you can sort alphabetically on title.
* Fields related to analytics, access and view counts.
* Add data_dict field for the whole package_dict.
* Add vocab_* dynamic field so it is possible to facet by vocabulary tags
* Add copyField for text with source vocab_*

v1.3 - (ckan>=1.5.1)
--------------------
Expand Down
2 changes: 2 additions & 0 deletions ckan/config/solr/schema-1.4.xml
Expand Up @@ -153,6 +153,7 @@
<field name="data_dict" type="string" indexed="false" stored="true" />

<dynamicField name="extras_*" type="text" indexed="true" stored="true" multiValued="false"/>
<dynamicField name="vocab_*" type="string" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*" type="string" indexed="true" stored="false"/>
</fields>

Expand All @@ -165,6 +166,7 @@
<copyField source="download_url" dest="urls"/>
<copyField source="res_url" dest="urls"/>
<copyField source="extras_*" dest="text"/>
<copyField source="vocab_*" dest="text"/>
<copyField source="urls" dest="text"/>
<copyField source="name" dest="text"/>
<copyField source="title" dest="text"/>
Expand Down
1 change: 1 addition & 0 deletions ckan/controllers/api.py
Expand Up @@ -251,6 +251,7 @@ def list(self, ver=None, register=None, subregister=None, id=None):
('dataset', 'activity'): 'package_activity_list',
('group', 'activity'): 'group_activity_list',
('user', 'activity'): 'user_activity_list',
('user', 'dashboard_activity'): 'dashboard_activity_list',
('activity', 'details'): 'activity_detail_list',
}

Expand Down
17 changes: 7 additions & 10 deletions ckan/controllers/datastore.py
@@ -1,9 +1,9 @@
from ckan.lib.base import BaseController, abort, _, c, response, request, g
import ckan.model as model
from ckan.lib.helpers import json
from ckan.lib.jsonp import jsonpify
from ckan.logic import get_action, check_access
from ckan.logic import NotFound, NotAuthorized, ValidationError
from ckan.logic import NotFound, NotAuthorized



class DatastoreController(BaseController):
Expand All @@ -21,8 +21,6 @@ def read(self, id, url=''):

try:
resource = get_action('resource_show')(context, {'id': id})
if not resource.get('webstore_url', ''):
return {'error': 'DataStore is disabled for this resource'}
self._make_redirect(id, url)
return ''
except NotFound:
Expand All @@ -35,13 +33,12 @@ def write(self, id, url):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author}
try:
resource = model.Resource.get(id)
if not resource:
abort(404, _('Resource not found'))
if not resource.webstore_url:
return {'error': 'DataStore is disabled for this resource'}
context["resource"] = resource
check_access('resource_update', context, {'id': id})
resource_dict = get_action('resource_show')(context,{'id':id})
if not resource_dict['webstore_url']:
resource_dict['webstore_url'] = u'active'
get_action('resource_update')(context,resource_dict)

self._make_redirect(id, url)
return ''
except NotFound:
Expand Down
5 changes: 2 additions & 3 deletions ckan/controllers/package.py
Expand Up @@ -594,8 +594,7 @@ def _save_new(self, context, package_type=None):
except DataError:
abort(400, _(u'Integrity Error'))
except SearchIndexError, e:
abort(500, _(u'Unable to add package to search index.') +
repr(e.args))
abort(500, _(u'Unable to add package to search index.'))
except ValidationError, e:
errors = e.error_dict
error_summary = e.error_summary
Expand Down Expand Up @@ -627,7 +626,7 @@ def _save_edit(self, name_or_id, context):
except DataError:
abort(400, _(u'Integrity Error'))
except SearchIndexError, e:
abort(500, _(u'Unable to update search index.') + repr(e.args))
abort(500, _(u'Unable to update search index.'))
except ValidationError, e:
errors = e.error_dict
error_summary = e.error_summary
Expand Down
2 changes: 1 addition & 1 deletion ckan/controllers/related.py
Expand Up @@ -34,5 +34,5 @@ def list(self, id):
base.abort(401, base._('Unauthorized to read package %s') % id)

c.related_count = len(c.pkg.related)

c.action = 'related'
return base.render("package/related_list.html")
17 changes: 15 additions & 2 deletions ckan/controllers/user.py
Expand Up @@ -5,6 +5,7 @@
from urllib import quote

import ckan.misc
import ckan.lib.i18n
from ckan.lib.base import *
from ckan.lib import mailer
from ckan.authz import Authorizer
Expand Down Expand Up @@ -117,8 +118,8 @@ def me(self, locale=None):
h.redirect_to(locale=locale, controller='user',
action='login', id=None)
user_ref = c.userobj.get_reference_preferred_for_uri()
h.redirect_to(locale=locale, controller='user',
action='read', id=user_ref)
h.redirect_to(locale=locale, controller='user', action='dashboard',
id=user_ref)

def register(self, data=None, errors=None, error_summary=None):
return self.new(data, errors, error_summary)
Expand Down Expand Up @@ -288,6 +289,11 @@ def logged_in(self):
# we need to set the language via a redirect
lang = session.pop('lang', None)
session.save()

# we need to set the language explicitly here or the flash
# messages will not be translated.
ckan.lib.i18n.set_lang(lang)

if c.user:
context = {'model': model,
'user': c.user}
Expand Down Expand Up @@ -445,3 +451,10 @@ def followers(self, id=None):
f = get_action('user_follower_list')
c.followers = f(context, {'id': c.user_dict['id']})
return render('user/followers.html')

def dashboard(self, id=None):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'for_view': True}
data_dict = {'id': id, 'user_obj': c.userobj}
self._setup_template_variables(context, data_dict)
return render('user/dashboard.html')
17 changes: 16 additions & 1 deletion ckan/lib/helpers.py
Expand Up @@ -506,7 +506,7 @@ def format_icon(_format):

def linked_gravatar(email_hash, size=100, default=None):
return literal(
'<a href="https://gravatar.com/" target="_blank"' +
'<a href="https://gravatar.com/" target="_blank" ' +
'title="%s">' % _('Update your avatar at gravatar.com') +
'%s</a>' % gravatar(email_hash,size,default)
)
Expand Down Expand Up @@ -871,6 +871,20 @@ def follow_count(obj_type, obj_id):
context = {'model' : model, 'session':model.Session, 'user':c.user}
return logic.get_action(action)(context, {'id': obj_id})

def dashboard_activity_stream(user_id):
'''Return the dashboard activity stream of the given user.
:param user_id: the id of the user
:type user_id: string
:returns: an activity stream as an HTML snippet
:rtype: string
'''
import ckan.logic as logic
context = {'model' : model, 'session':model.Session, 'user':c.user}
return logic.get_action('dashboard_activity_list_html')(context, {'id': user_id})


# these are the functions that will end up in `h` template helpers
# if config option restrict_template_vars is true
Expand Down Expand Up @@ -927,6 +941,7 @@ def follow_count(obj_type, obj_id):
'unselected_facet_items',
'follow_button',
'follow_count',
'dashboard_activity_stream',
# imported into ckan.lib.helpers
'literal',
'link_to',
Expand Down
50 changes: 46 additions & 4 deletions ckan/lib/i18n.py
Expand Up @@ -12,14 +12,26 @@
# we don't have a Portuguese territory
# translation currently.

def _get_locales():
def get_locales_from_config():
''' despite the name of this function it gets the locales defined by
the config AND also the locals available subject to the config. '''
locales_offered = config.get('ckan.locales_offered', '').split()
filtered_out = config.get('ckan.locales_filtered_out', '').split()
locale_default = config.get('ckan.locale_default', 'en')
locale_order = config.get('ckan.locale_order', '').split()
known_locales = get_locales()
all_locales = set(known_locales) | set(locales_offered) | set(locale_order) | set(locale_default)
all_locales -= set(filtered_out)
return all_locales

def _get_locales():
# FIXME this wants cleaning up and merging with get_locales_from_config()
assert not config.get('lang'), \
'"lang" config option not supported - please use ckan.locale_default instead.'
locales_offered = config.get('ckan.locales_offered', '').split()
filtered_out = config.get('ckan.locales_filtered_out', '').split()
locale_order = config.get('ckan.locale_order', '').split()
locale_default = config.get('ckan.locale_default', 'en')
locale_order = config.get('ckan.locale_order', '').split()

locales = ['en']
i18n_path = os.path.dirname(ckan.i18n.__file__)
Expand Down Expand Up @@ -58,6 +70,7 @@ def _get_locales():
available_locales = None
locales = None
locales_dict = None
_non_translated_locals = None

def get_locales():
''' Get list of available locales
Expand All @@ -68,6 +81,15 @@ def get_locales():
locales = _get_locales()
return locales

def non_translated_locals():
''' These are the locales that are available but for which there are
no translations. returns a list like ['en', 'de', ...] '''
global _non_translated_locals
if not _non_translated_locals:
locales = config.get('ckan.locale_order', '').split()
_non_translated_locals = [x for x in locales if x not in get_locales()]
return _non_translated_locals

def get_locales_dict():
''' Get a dict of the available locales
e.g. { 'en' : Locale('en'), 'de' : Locale('de'), ... } '''
Expand All @@ -87,12 +109,25 @@ def get_available_locales():
available_locales = map(Locale.parse, get_locales())
return available_locales

def _set_lang(lang):
''' Allows a custom i18n directory to be specified.
Creates a fake config file to pass to pylons.i18n.set_lang, which
sets the Pylons root path to desired i18n_directory.
This is needed as Pylons will only look for an i18n directory in
the application root.'''
if config.get('ckan.i18n_directory'):
fake_config = {'pylons.paths': {'root': config['ckan.i18n_directory']},
'pylons.package': config['pylons.package']}
i18n.set_lang(lang, pylons_config=fake_config)
else:
i18n.set_lang(lang)

def handle_request(request, tmpl_context):
''' Set the language for the request '''
lang = request.environ.get('CKAN_LANG') or \
config.get('ckan.locale_default', 'en')
config.get('ckan.locale_default', 'en')
if lang != 'en':
i18n.set_lang(lang)
set_lang(lang)
tmpl_context.language = lang
return lang

Expand All @@ -104,3 +139,10 @@ def get_lang():
return langs[0]
else:
return 'en'

def set_lang(language_code):
''' Wrapper to pylons call '''
if language_code in non_translated_locals():
language_code = config.get('ckan.locale_default', 'en')
if language_code != 'en':
_set_lang(language_code)
26 changes: 21 additions & 5 deletions ckan/lib/search/index.py
@@ -1,7 +1,6 @@
import socket
import string
import logging
import itertools
import collections
import json

Expand All @@ -14,6 +13,7 @@
import ckan.model as model
from ckan.plugins import (PluginImplementations,
IPackageController)
import ckan.logic as logic

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -122,10 +122,27 @@ def index_package(self, pkg_dict):
pkg_dict[key] = value
pkg_dict.pop('extras', None)

#Add tags and groups
# add tags, removing vocab tags from 'tags' list and adding them as
# vocab_<tag name> so that they can be used in facets
non_vocab_tag_names = []
tags = pkg_dict.pop('tags', [])
pkg_dict['tags'] = [tag['name'] for tag in tags]

context = {'model': model}

for tag in tags:
if tag.get('vocabulary_id'):
data = {'id': tag['vocabulary_id']}
vocab = logic.get_action('vocabulary_show')(context, data)
key = u'vocab_%s' % vocab['name']
if key in pkg_dict:
pkg_dict[key].append(tag['name'])
else:
pkg_dict[key] = [tag['name']]
else:
non_vocab_tag_names.append(tag['name'])

pkg_dict['tags'] = non_vocab_tag_names

# add groups
groups = pkg_dict.pop('groups', [])

# Capacity is different to the default only if using organizations
Expand Down Expand Up @@ -197,7 +214,6 @@ def index_package(self, pkg_dict):
import hashlib
pkg_dict['index_id'] = hashlib.md5('%s%s' % (pkg_dict['id'],config.get('ckan.site_id'))).hexdigest()


for item in PluginImplementations(IPackageController):
pkg_dict = item.before_index(pkg_dict)

Expand Down

0 comments on commit 3676cfe

Please sign in to comment.