Skip to content

Commit

Permalink
Merge branch 'master' into 3016-template-tweaks
Browse files Browse the repository at this point in the history
Conflicts:
	ckan/public/base/less/masthead.less
	ckan/templates/header.html

trivial
  • Loading branch information
tobes committed Nov 22, 2012
2 parents fac6a66 + c81ee8f commit 11081ae
Show file tree
Hide file tree
Showing 48 changed files with 2,820 additions and 2,523 deletions.
50 changes: 30 additions & 20 deletions README.rst
@@ -1,35 +1,45 @@
CKAN is open-source data hub software. CKAN makes it easy to publish, share and
work with data. It's a data management system that provides a powerful platform
for cataloging, storing and accessing datasets with a rich front-end, full API
(for both data and catalog), visualization tools and more. Read more at
http://ckan.org/.
CKAN: The Open Source Data Portal Software
==========================================

* Installation instructions: see docs at http://docs.ckan.org/
* Project wiki: http://wiki.ckan.org/
* Developer mailing list: ckan-dev@lists.okfn.org
* Issue tracker: http://trac.ckan.org/
**CKAN is the world’s leading open-source data portal platform**.
CKAN makes it easy to publish, share and work with data. It's a data management
system that provides a powerful platform for cataloging, storing and accessing
datasets with a rich front-end, full API (for both data and catalog), visualization
tools and more. Read more at `ckan.org <http://ckan.org/>`_.

Building Documentation
======================

1. Install python-sphinx (>= 1.1)
Installation
------------

2. Initialize the theme submodule::
See the `CKAN Documentation <http://docs.ckan.org>`_ for installation instructions.

git submodule init
git submodule update

3. Run the command to build the docs::
Community
---------

* Developer mailing list: `ckan-dev@lists.okfn.org <http://lists.okfn.org/mailman/listinfo/ckan-dev>`_
* Developer IRC channel: #ckan on `irc.freenode.net <http://freenode.net/>`_
* Issue tracker: `trac.ckan.org <http://trac.ckan.org/>`_
* `CKAN tag on StackOverflow <http://stackoverflow.com/questions/tagged/ckan>`_


Contributing to CKAN
--------------------

CKAN is a free software project and code contributions are welcome.
The `For CKAN Developers <http://docs.ckan.org/en/latest/index.html#for-ckan-developers>`_
section of the documentation explains how to contribute to CKAN or its documentation,
including our **coding standards**.

The `CKAN Wiki <https://github.com/okfn/ckan/wiki>`_ is also open fo contributions.

python setup.py build_sphinx

Copying and License
===================
-------------------

This material is copyright (c) 2006-2011 Open Knowledge Foundation.

It is open and licensed under the GNU Affero General Public License (AGPL) v3.0
whose full text may be found at:

http://www.fsf.org/licensing/licenses/agpl-3.0.html

http://www.fsf.org/licensing/licenses/agpl-3.0.html
8 changes: 8 additions & 0 deletions ckan/config/deployment.ini_tmpl
Expand Up @@ -235,6 +235,14 @@ ckan.feeds.author_link =
#ofs.aws_access_key_id = ....
#ofs.aws_secret_access_key = ....


# DEBUGGING

# ckan.debug_supress_header This option can be set to suppress the debug
# information showing the controller and action recieving the request being
# shown in the header. Note: This info only shows if debug is set to true.
ckan.debug_supress_header = false

## ===================================
## Extensions

Expand Down
6 changes: 3 additions & 3 deletions ckan/controllers/group.py
Expand Up @@ -554,18 +554,18 @@ def unfollow(self, id):
h.flash_error(error_message)
h.redirect_to(controller='group', action='read', id=id)

def followers(self, id=None):
def followers(self, id):
context = self._get_group_dict(id)
c.followers = get_action('group_follower_list')(context,
{'id': c.group_dict['id']})
return render('group/followers.html')

def admins(self, id=None):
def admins(self, id):
context = self._get_group_dict(id)
c.admins = self.authorizer.get_admins(context['group'])
return render('group/admins.html')

def about(self, id=None):
def about(self, id):
self._get_group_dict(id)
return render('group/about.html')

Expand Down
7 changes: 7 additions & 0 deletions ckan/controllers/user.py
Expand Up @@ -500,6 +500,13 @@ def dashboard(self, id=None):
'user': c.user or c.author, 'for_view': True}
data_dict = {'id': id, 'user_obj': c.userobj}
self._setup_template_variables(context, data_dict)

c.dashboard_activity_stream = h.dashboard_activity_stream(id)

# Mark the user's new activities as old whenever they view their
# dashboard page.
get_action('dashboard_mark_all_new_activities_as_old')(context, {})

return render('user/dashboard.html')

def follow(self, id):
Expand Down
5 changes: 4 additions & 1 deletion ckan/lib/activity_streams.py
@@ -1,4 +1,5 @@
import re
import datetime

from pylons.i18n import _
from webhelpers.html import literal
Expand Down Expand Up @@ -228,10 +229,12 @@ def activity_list_to_html(context, activity_stream):
for match in matches:
snippet = activity_snippet_functions[match](activity, detail)
data[str(match)] = snippet

activity_list.append({'msg': activity_msg,
'type': activity_type.replace(' ', '-').lower(),
'icon': activity_icon,
'data': data,
'timestamp': activity['timestamp']})
'timestamp': activity['timestamp'],
'is_new': activity.get('is_new', False)})
return literal(base.render('activity_streams/activity_stream_items.html',
extra_vars={'activities': activity_list}))
2 changes: 2 additions & 0 deletions ckan/lib/app_globals.py
Expand Up @@ -161,5 +161,7 @@ def _init(self):
datasets_per_page = int(config.get('ckan.datasets_per_page', '20'))
self.datasets_per_page = datasets_per_page

self.debug_supress_header = asbool(config.get('ckan.debug_supress_header', 'false'))

app_globals = _Globals()
del _Globals
10 changes: 10 additions & 0 deletions ckan/lib/base.py
Expand Up @@ -214,6 +214,16 @@ def __before__(self, action, **params):
self._identify_user()
i18n.handle_request(request, c)

# If the user is logged in add their number of new activities to the
# template context.
if c.userobj:
from ckan.logic import get_action
new_activities_count = get_action(
'dashboard_new_activities_count')
context = {'model': model, 'session': model.Session,
'user': c.user or c.author}
c.new_activities = new_activities_count(context, {})

def _identify_user(self):
'''
Identifies the user using two methods:
Expand Down
9 changes: 9 additions & 0 deletions ckan/lib/cli.py
Expand Up @@ -130,6 +130,15 @@ def command(self):
if self.verbose:
print 'Initialising DB: SUCCESS'
elif cmd == 'clean' or cmd == 'drop':

# remove any *.pyc version files to prevent conflicts
v_path = os.path.join(os.path.dirname(__file__),
'..', 'migration', 'versions', '*.pyc')
import glob
filelist = glob.glob(v_path)
for f in filelist:
os.remove(f)

model.repo.clean_db()
search.clear()
if self.verbose:
Expand Down
122 changes: 80 additions & 42 deletions ckan/logic/action/get.py
@@ -1,6 +1,7 @@
import uuid
import logging
import json
import datetime

from pylons import config
from pylons.i18n import _
Expand Down Expand Up @@ -1734,11 +1735,7 @@ def user_activity_list(context, data_dict):
_check_access('user_show', context, data_dict)
model = context['model']
user_id = _get_or_bust(data_dict, 'id')
query = model.Session.query(model.Activity)
query = query.filter_by(user_id=user_id)
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
activity_objects = model.activity.user_activity_list(user_id)
return model_dictize.activity_list_dictize(activity_objects, context)

def package_activity_list(context, data_dict):
Expand All @@ -1757,11 +1754,7 @@ def package_activity_list(context, data_dict):
_check_access('package_show', context, data_dict)
model = context['model']
package_id = _get_or_bust(data_dict, 'id')
query = model.Session.query(model.Activity)
query = query.filter_by(object_id=package_id)
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
activity_objects = model.activity.package_activity_list(package_id)
return model_dictize.activity_list_dictize(activity_objects, context)

def group_activity_list(context, data_dict):
Expand All @@ -1786,15 +1779,26 @@ def group_activity_list(context, data_dict):
group_show = logic.get_action('group_show')
group_id = group_show(context, {'id': group_id})['id']

# FIXME: The SQLAlchemy below should be moved into ckan/model/activity.py
# (to encapsulate SQLALchemy in the model and avoid using it from the
# logic) but it can't be because it requires the list of dataset_ids which
# comes from logic.group_package_show() (and I don't want to access the
# logic from the model). Need to change it to get the dataset_ids from the
# model instead. There seems to be multiple methods for getting a group's
# datasets, some in the logic and some in the model.

# Get a list of the IDs of the group's datasets.
group_package_show = logic.get_action('group_package_show')
datasets = group_package_show(context, {'id': group_id})
dataset_ids = [dataset['id'] for dataset in datasets]

# Get the group's activities.
query = model.Session.query(model.Activity)
query = query.filter(_or_(model.Activity.object_id == group_id,
model.Activity.object_id.in_(dataset_ids)))
if dataset_ids:
query = query.filter(_or_(model.Activity.object_id == group_id,
model.Activity.object_id.in_(dataset_ids)))
else:
query = query.filter(model.Activity.object_id == group_id)
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
Expand All @@ -1810,11 +1814,7 @@ def recently_changed_packages_activity_list(context, data_dict):
# FIXME: Filter out activities whose subject or object the user is not
# authorized to read.
model = context['model']
query = model.Session.query(model.Activity)
query = query.filter(model.Activity.activity_type.endswith('package'))
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
activity_objects = model.activity.recently_changed_packages_activity_list()
return model_dictize.activity_list_dictize(activity_objects, context)

def activity_detail_list(context, data_dict):
Expand Down Expand Up @@ -2199,52 +2199,90 @@ def group_followee_list(context, data_dict):


def dashboard_activity_list(context, data_dict):
'''Return the dashboard activity stream of the given user.
'''Return the authorized user's dashboard activity stream.
:param id: the id or name of the user
:type id: string
Unlike the activity dictionaries returned by other *_activity_list actions,
these activity dictionaries have an extra boolean value with key 'is_new'
that tells you whether the activity happened since the user last viewed her
dashboard ('is_new': True) or not ('is_new': False).
:rtype: list of dictionaries
The user's own activities are always marked 'is_new': False.
:rtype: list of activity dictionaries
'''
# FIXME: Filter out activities whose subject or object the user is not
# authorized to read.
model = context['model']
user_id = _get_or_bust(data_dict, 'id')
_check_access('dashboard_activity_list', context, data_dict)

activity_query = model.Session.query(model.Activity)
user_followees_query = activity_query.join(model.UserFollowingUser, model.UserFollowingUser.object_id == model.Activity.user_id)
dataset_followees_query = activity_query.join(model.UserFollowingDataset, model.UserFollowingDataset.object_id == model.Activity.object_id)
model = context['model']
user_id = model.User.get(context['user']).id

from_user_query = activity_query.filter(model.Activity.user_id==user_id)
about_user_query = activity_query.filter(model.Activity.object_id==user_id)
user_followees_query = user_followees_query.filter(model.UserFollowingUser.follower_id==user_id)
dataset_followees_query = dataset_followees_query.filter(model.UserFollowingDataset.follower_id==user_id)
# FIXME: Filter out activities whose subject or object the user is not
# authorized to read.
activity_objects = model.activity.dashboard_activity_list(user_id)

activity_dicts = model_dictize.activity_list_dictize(
activity_objects, context)

# Mark the new (not yet seen by user) activities.
strptime = datetime.datetime.strptime
fmt = '%Y-%m-%dT%H:%M:%S.%f'
last_viewed = model.Dashboard.get_activity_stream_last_viewed(user_id)
for activity in activity_dicts:
if activity['user_id'] == user_id:
# Never mark the user's own activities as new.
activity['is_new'] = False
else:
activity['is_new'] = (strptime(activity['timestamp'], fmt)
> last_viewed)

query = from_user_query.union(about_user_query).union(
user_followees_query).union(dataset_followees_query)
query = query.order_by(_desc(model.Activity.timestamp))
query = query.limit(15)
activity_objects = query.all()
return activity_dicts

return model_dictize.activity_list_dictize(activity_objects, context)

def dashboard_activity_list_html(context, data_dict):
'''Return the dashboard activity stream of the given user as HTML.
'''Return the authorized user's dashboard activity stream as HTML.
The activity stream is rendered as a snippet of HTML meant to be included
in an HTML page, i.e. it doesn't have any HTML header or footer.
:param id: The id or name of the user.
:type id: string
:rtype: string
'''
activity_stream = dashboard_activity_list(context, data_dict)
return activity_streams.activity_list_to_html(context, activity_stream)


def dashboard_new_activities_count(context, data_dict):
'''Return the number of new activities in the user's dashboard.
Return the number of new activities in the authorized user's dashboard
activity stream.
Activities from the user herself are not counted by this function even
though they appear in the dashboard (users don't want to be notified about
things they did themselves).
:rtype: int
'''
_check_access('dashboard_new_activities_count', context, data_dict)
activities = logic.get_action('dashboard_activity_list')(
context, data_dict)
return len([activity for activity in activities if activity['is_new']])


def dashboard_mark_all_new_activities_as_old(context, data_dict):
'''Mark all the authorized user's new dashboard activities as old.
This will reset dashboard_new_activities_count to 0.
'''
_check_access('dashboard_mark_all_new_activities_as_old', context,
data_dict)
model = context['model']
user_id = model.User.get(context['user']).id
model.Dashboard.update_activity_stream_last_viewed(user_id)


def _unpick_search(sort, allowed_fields=None, total=None):
''' This is a helper function that takes a sort string
eg 'name asc, last_modified desc' and returns a list of
Expand Down
24 changes: 24 additions & 0 deletions ckan/logic/auth/get.py
Expand Up @@ -189,3 +189,27 @@ def get_site_user(context, data_dict):
return {'success': False, 'msg': 'Only internal services allowed to use this action'}
else:
return {'success': True}


def dashboard_activity_list(context, data_dict):
# FIXME: context['user'] could be an IP address but that case is not
# handled here. Maybe add an auth helper function like is_logged_in().
if context.get('user'):
return {'success': True}
else:
return {'success': False,
'msg': _("You must be logged in to access your dashboard.")}


def dashboard_new_activities_count(context, data_dict):
# FIXME: This should go through check_access() not call is_authorized()
# directly, but wait until 2939-orgs is merged before fixing this.
return ckan.new_authz.is_authorized('dashboard_activity_list',
context, data_dict)


def dashboard_mark_all_new_activities_as_old(context, data_dict):
# FIXME: This should go through check_access() not call is_authorized()
# directly, but wait until 2939-orgs is merged before fixing this.
return ckan.new_authz.is_authorized('dashboard_activity_list',
context, data_dict)

0 comments on commit 11081ae

Please sign in to comment.