Skip to content

Commit

Permalink
[merge] from release-v1.4.1.
Browse files Browse the repository at this point in the history
  • Loading branch information
David Read committed Jun 27, 2011
2 parents 04f836c + 10b3d77 commit f26c876
Show file tree
Hide file tree
Showing 64 changed files with 1,626 additions and 2,197 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
CKAN CHANGELOG
++++++++++++++

v1.4.2 2011-XX-XX
=================
Major:
* Packages revisions can be marked as 'moderated' (#1141)
* Viewing of a package at any revision (#1141)


v1.4.1 2011-06-27
=================
Major:
Expand Down
2 changes: 1 addition & 1 deletion ckan/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '1.4.1'
__version__ = '1.4.2a'
__description__ = 'Comprehensive Knowledge Archive Network (CKAN) Software'
__long_description__ = \
'''The CKAN software is used to run the Comprehensive Knowledge Archive
Expand Down
18 changes: 12 additions & 6 deletions ckan/authz.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def is_authorized(cls, username, action, domain_object):

# check it's active
if domain_object.__class__ != type and hasattr(domain_object, 'state'):
if domain_object.state != model.State.ACTIVE:
if domain_object.state == model.State.DELETED:
return False

# check if any of the roles allows the action requested
Expand Down Expand Up @@ -184,7 +184,6 @@ def authorized_query(cls, username, entity, action=model.Action.READ):
user = model.User.by_name(username, autoflush=False)
else:
user = None
entity.roles.property.mapper.class_
visitor = model.User.by_name(model.PSEUDO_USER__VISITOR, autoflush=False)
logged_in = model.User.by_name(model.PSEUDO_USER__LOGGED_IN,
autoflush=False)
Expand All @@ -193,8 +192,15 @@ def authorized_query(cls, username, entity, action=model.Action.READ):
# need to use this in the queries below as if we use
# model.UserObjectRole a cross join happens always
# returning all the roles.
role_cls = entity.roles.property.mapper.class_
q = q.outerjoin('roles')
if hasattr(entity, 'continuity'):
q = q.filter_by(current=True)
q = q.outerjoin('continuity', 'roles')
continuity = entity.continuity.property.mapper.class_
role_cls = continuity.roles.property.mapper.class_
else:
role_cls = entity.roles.property.mapper.class_
q = q.outerjoin('roles')

if hasattr(entity, 'state'):
state = entity.state
else:
Expand All @@ -209,13 +215,13 @@ def authorized_query(cls, username, entity, action=model.Action.READ):
q = q.filter(sa.or_(
sa.and_(role_cls.role==model.RoleAction.role,
model.RoleAction.action==action,
state and state==model.State.ACTIVE),
state and state!=model.State.DELETED),
role_cls.role==model.Role.ADMIN))
else:
q = q.filter(
sa.and_(role_cls.role==model.RoleAction.role,
model.RoleAction.action==action,
state and state==model.State.ACTIVE),
state and state!=model.State.DELETED),
)
q = q.filter(sa.or_(*filters))
q = q.distinct()
Expand Down
7 changes: 6 additions & 1 deletion ckan/config/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,14 @@ def make_map():
]))
)
map.connect('/package', controller='package', action='index')
map.connect('/package/{action}/{id}/{revision}', controller='package', action='read_ajax')
map.connect('/package/{action}/{id}', controller='package',
requirements=dict(action='|'.join([
'edit',
'authz',
'history'
'history',
'read_ajax',
'history_ajax',
]))
)
map.connect('/package/{id}', controller='package', action='read')
Expand Down Expand Up @@ -227,10 +230,12 @@ def make_map():
# Note: openid users have slashes in their ids, so need the wildcard
# in the route.
map.connect('/user/edit/{id:.*}', controller='user', action='edit')
map.connect('/user/reset/{id:.*}', controller='user', action='perform_reset')
map.connect('/user/register', controller='user', action='register')
map.connect('/user/login', controller='user', action='login')
map.connect('/user/logged_in', controller='user', action='logged_in')
map.connect('/user/logged_out', controller='user', action='logged_out')
map.connect('/user/reset', controller='user', action='request_reset')
map.connect('/user/me', controller='user', action='me')
map.connect('/user/{id:.*}', controller='user', action='read')
map.connect('/user', controller='user', action='index')
Expand Down
4 changes: 2 additions & 2 deletions ckan/controllers/group_formalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def new(self):
for extension in self.extensions:
extension.create(group)
model.repo.commit_and_remove()
h.redirect_to(action='read', id=c.groupname)
h.redirect_to(controller='group', action='read', id=c.groupname)

if request.params:
data = forms.edit_group_dict(ckan.forms.get_group_dict(), request.params)
Expand Down Expand Up @@ -122,4 +122,4 @@ def edit(self, id=None): # allow id=None to allow posting
for extension in self.extensions:
extension.edit(group)
model.repo.commit_and_remove()
h.redirect_to(action='read', id=c.groupname)
h.redirect_to(controller='group', action='read', id=c.groupname)
8 changes: 4 additions & 4 deletions ckan/controllers/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from genshi.template import NewTextTemplate

from ckan.authz import Authorizer
from ckan.logic.action.get import current_package_list_with_resources
from ckan.i18n import set_session_locale
from ckan.lib.search import query_for, QueryOptions, SearchError
from ckan.lib.cache import proxy_cache, get_cache_expires
Expand Down Expand Up @@ -46,10 +47,9 @@ def index(self):
c.facets = query.facets
c.fields = []
c.package_count = query.count
c.latest_packages = self.authorizer.authorized_query(c.user, model.Package)\
.join('revision').order_by(model.Revision.timestamp.desc())\
.limit(5).all()

c.latest_packages = current_package_list_with_resources({'model': model,
'user': c.user,
'limit': 5})
return render('home/index.html', cache_key=cache_key,
cache_expire=cache_expires)

Expand Down
120 changes: 95 additions & 25 deletions ckan/controllers/package.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import logging
import urlparse
from urllib import urlencode
import json
import datetime
import re

from sqlalchemy.orm import eagerload_all
from sqlalchemy import or_
import genshi
from pylons import config, cache
from pylons.i18n import get_lang, _
from autoneg.accept import negotiate
from babel.dates import format_date, format_datetime, format_time

import ckan.logic.action.create as create
import ckan.logic.action.update as update
Expand All @@ -21,8 +25,9 @@
from ckan.lib.package_saver import PackageSaver, ValidationException
from ckan.lib.navl.dictization_functions import DataError, unflatten, validate
from ckan.logic import NotFound, NotAuthorized, ValidationError
from ckan.logic import tuplize_dict, clean_dict, parse_params
from ckan.logic import tuplize_dict, clean_dict, parse_params, flatten_to_string_key
from ckan.plugins import PluginImplementations, IPackageController
from ckan.lib.dictization import table_dictize
import ckan.forms
import ckan.authz
import ckan.rating
Expand Down Expand Up @@ -171,11 +176,26 @@ def _clear_pkg_cache(self, pkg):

@proxy_cache()
def read(self, id):

context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'extras_as_string': True,
'schema': self._form_to_db_schema(),
'id': id}
split = id.split('@')
if len(split) == 2:
context['id'], revision = split
try:
date = datetime.datetime(*map(int, re.split('[^\d]', revision)))
context['revision_date'] = date
except ValueError:
context['revision_id'] = revision
#check if package exists
c.pkg = model.Package.get(id)
if c.pkg is None:
try:
c.pkg_dict = get.package_show(context)
c.pkg = context['package']
except NotFound:
abort(404, _('Package not found'))
except NotAuthorized:
abort(401, _('Unauthorized to read package %s') % id)

cache_key = self._pkg_cache_key(c.pkg)
etag_cache(cache_key)
Expand All @@ -194,38 +214,33 @@ def read(self, id):
rdf_url = '%s%s.%s' % (config['rdf_packages'], c.pkg.id, exts[0])
redirect(rdf_url, code=303)
break

#is the user allowed to see this package?
auth_for_read = self.authorizer.am_authorized(c, model.Action.READ, c.pkg)
if not auth_for_read:
abort(401, _('Unauthorized to read package %s') % id)

#render the package
PackageSaver().render_package(c.pkg)
for item in self.extensions:
item.read(c.pkg)

PackageSaver().render_package(c.pkg_dict, context)
return render('package/read.html')

def comments(self, id):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'extras_as_string': True,
'schema': self._form_to_db_schema(),
'id': id}

#check if package exists
c.pkg = model.Package.get(id)
if c.pkg is None:
try:
c.pkg_dict = get.package_show(context)
c.pkg = context['package']
except NotFound:
abort(404, _('Package not found'))
except NotAuthorized:
abort(401, _('Unauthorized to read package %s') % id)

# used by disqus plugin
c.current_package_id = c.pkg.id

#is the user allowed to see this package?
auth_for_read = self.authorizer.am_authorized(c, model.Action.READ, c.pkg)
if not auth_for_read:
abort(401, _('Unauthorized to read package %s') % id)

for item in self.extensions:
item.read(c.pkg)

#render the package
PackageSaver().render_package(c.pkg)
PackageSaver().render_package(c.pkg_dict)
return render('package/comments.html')


Expand Down Expand Up @@ -318,7 +333,8 @@ def edit(self, id, data=None, errors=None, error_summary=None):
'user': c.user or c.author, 'extras_as_string': True,
'preview': 'preview' in request.params,
'save': 'save' in request.params,
'id': id,
'id': id, 'moderated': config.get('moderated'),
'pending': True,
'schema': self._form_to_db_schema()}

if (context['save'] or context['preview']) and not data:
Expand Down Expand Up @@ -347,6 +363,53 @@ def edit(self, id, data=None, errors=None, error_summary=None):
c.form = render(self.package_form, extra_vars=vars)
return render('package/edit.html')

def read_ajax(self, id, revision=None):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author,
'id': id, 'extras_as_string': True,
'schema': self._form_to_db_schema(),
'revision_id': revision}

try:
data = get.package_show(context)
schema = self._db_to_form_schema()
if schema:
data, errors = validate(data, schema)
except NotAuthorized:
abort(401, _('Unauthorized to read package %s') % '')

## hack as db_to_form schema should have this
data['tag_string'] = ' '.join([tag['name'] for tag in data.get('tags', [])])
data.pop('tags')
data = flatten_to_string_key(data)
response.headers['Content-Type'] = 'application/json;charset=utf-8'
return json.dumps(data)

def history_ajax(self, id):

context = {'model': model, 'session': model.Session,
'user': c.user or c.author,
'id': id, 'extras_as_string': True}
pkg = model.Package.get(id)
data = []
approved = False
for num, (revision, revision_obj) in enumerate(pkg.all_related_revisions):
if not approved and revision.approved_timestamp:
current_approved, approved = True, True
else:
current_approved = False

data.append({'revision_id': revision.id,
'message': revision.message,
'timestamp': format_datetime(revision.timestamp,
locale=(get_lang() or ['en'])[0]),
'author': revision.author,
'approved': bool(revision.approved_timestamp),
'current_approved': current_approved})

response.headers['Content-Type'] = 'application/json;charset=utf-8'
return json.dumps(data)

def _save_new(self, context):
try:
data_dict = clean_dict(unflatten(
Expand All @@ -356,7 +419,9 @@ def _save_new(self, context):
pkg = create.package_create(data_dict, context)

if context['preview']:
PackageSaver().render_package(context['package'])
PackageSaver().render_package(pkg, context)
c.pkg = context['package']
c.pkg_dict = data_dict
c.is_preview = True
c.preview = render('package/read_core.html')
return self.new(data_dict)
Expand All @@ -379,12 +444,17 @@ def _save_edit(self, id, context):
tuplize_dict(parse_params(request.POST))))
self._check_data_dict(data_dict)
context['message'] = data_dict.get('log_message', '')
if not context['moderated']:
context['pending'] = False
pkg = update.package_update(data_dict, context)
if request.params.get('save', '') == 'Approve':
update.make_latest_pending_package_active(context)
c.pkg = context['package']
c.pkg_dict = pkg

if context['preview']:
c.is_preview = True
PackageSaver().render_package(context['package'])
PackageSaver().render_package(pkg, context)
c.preview = render('package/read_core.html')
return self.edit(id, data_dict)

Expand Down
37 changes: 35 additions & 2 deletions ckan/controllers/user.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import re
import logging

import genshi
Expand All @@ -7,6 +6,7 @@

import ckan.misc
from ckan.lib.base import *
from ckan.lib import mailer

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -183,7 +183,40 @@ def edit(self, id=None):
h.redirect_to(controller='user', action='read', id=user.id)

return render('user/edit.html')


def request_reset(self):
if request.method == 'POST':
id = request.params.get('user')
user = model.User.get(id)
if user is None:
h.flash_error(_('No such user: %s') % id)
try:
mailer.send_reset_link(user)
h.flash_success(_('Please check your inbox for a reset code.'))
redirect('/')
except mailer.MailerException, e:
h.flash_error(_('Could not send reset link: %s') % unicode(e))
return render('user/request_reset.html')

def perform_reset(self, id):
user = model.User.get(id)
if user is None:
abort(404)
c.reset_key = request.params.get('key')
if not mailer.verify_reset_link(user, c.reset_key):
h.flash_error(_('Invalid reset key. Please try again.'))
abort(403)
if request.method == 'POST':
try:
user.password = self._get_form_password()
model.Session.add(user)
model.Session.commit()
h.flash_success(_("Your password has been reset."))
redirect('/')
except ValueError, ve:
h.flash_error(unicode(ve))
return render('user/perform_reset.html')

def _format_about(self, about):
about_formatted = ckan.misc.MarkdownFormat().to_html(about)
try:
Expand Down
Loading

0 comments on commit f26c876

Please sign in to comment.