You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by tv...@apache.org on 2014/01/10 19:19:50 UTC
[26/32] PEP8 cleanup
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 65b68c8..6e5ef42 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -40,7 +40,7 @@ log = logging.getLogger(__name__)
class RepositoryApp(Application):
- END_OF_REF_ESCAPE='~'
+ END_OF_REF_ESCAPE = '~'
__version__ = version.__version__
permissions = [
'read', 'write', 'create',
@@ -56,19 +56,19 @@ class RepositoryApp(Application):
ConfigOption('cloned_from_project_id', ObjectId, None),
ConfigOption('cloned_from_repo_id', ObjectId, None),
ConfigOption('init_from_url', str, None)
- ]
- tool_label='Repository'
- default_mount_label='Code'
- default_mount_point='code'
- relaxed_mount_points=True
- ordinal=2
- forkable=False
- default_branch_name=None # master or default or some such
- repo=None # override with a property in child class
- icons={
- 24:'images/code_24.png',
- 32:'images/code_32.png',
- 48:'images/code_48.png'
+ ]
+ tool_label = 'Repository'
+ default_mount_label = 'Code'
+ default_mount_point = 'code'
+ relaxed_mount_points = True
+ ordinal = 2
+ forkable = False
+ default_branch_name = None # master or default or some such
+ repo = None # override with a property in child class
+ icons = {
+ 24: 'images/code_24.png',
+ 32: 'images/code_32.png',
+ 48: 'images/code_48.png'
}
def __init__(self, project, config):
@@ -79,9 +79,9 @@ class RepositoryApp(Application):
'''Apps should provide their entries to be added to the main nav
:return: a list of :class:`SitemapEntries <allura.app.SitemapEntry>`
'''
- return [ SitemapEntry(
- self.config.options.mount_label,
- '.')]
+ return [SitemapEntry(
+ self.config.options.mount_label,
+ '.')]
@property
@h.exceptionless([], log)
@@ -89,13 +89,17 @@ class RepositoryApp(Application):
menu_id = self.config.options.mount_label
with h.push_config(c, app=self):
return [
- SitemapEntry(menu_id, '.')[self.sidebar_menu()] ]
+ SitemapEntry(menu_id, '.')[self.sidebar_menu()]]
def admin_menu(self):
- admin_url = c.project.url()+'admin/'+self.config.options.mount_point+'/'
- links = [SitemapEntry('Viewable Files', admin_url + 'extensions', className='admin_modal')]
+ admin_url = c.project.url() + 'admin/' + \
+ self.config.options.mount_point + '/'
+ links = [
+ SitemapEntry('Viewable Files', admin_url + 'extensions', className='admin_modal')]
links.append(SitemapEntry('Refresh Repository',
- c.project.url() + self.config.options.mount_point + '/refresh',
+ c.project.url() +
+ self.config.options.mount_point +
+ '/refresh',
))
links += super(RepositoryApp, self).admin_menu()
[links.remove(l) for l in links[:] if l.label == 'Options']
@@ -105,41 +109,47 @@ class RepositoryApp(Application):
def sidebar_menu(self):
if not self.repo or self.repo.status != 'ready':
return []
- links = [SitemapEntry('Browse Commits', c.app.url + 'commit_browser', ui_icon=g.icons['folder'])]
+ links = [SitemapEntry('Browse Commits', c.app.url +
+ 'commit_browser', ui_icon=g.icons['folder'])]
if self.forkable and self.repo.status == 'ready':
- links.append(SitemapEntry('Fork', c.app.url + 'fork', ui_icon=g.icons['fork']))
- merge_request_count = self.repo.merge_requests_by_statuses('open').count()
+ links.append(
+ SitemapEntry('Fork', c.app.url + 'fork', ui_icon=g.icons['fork']))
+ merge_request_count = self.repo.merge_requests_by_statuses(
+ 'open').count()
if merge_request_count:
links += [
SitemapEntry(
'Merge Requests', c.app.url + 'merge-requests/',
- small=merge_request_count) ]
+ small=merge_request_count)]
if self.repo.forks:
links += [
- SitemapEntry('Forks', c.app.url + 'forks/', small=len(self.repo.forks))
+ SitemapEntry('Forks', c.app.url + 'forks/',
+ small=len(self.repo.forks))
]
if self.repo.upstream_repo.name:
- repo_path_parts = self.repo.upstream_repo.name.strip('/').split('/')
+ repo_path_parts = self.repo.upstream_repo.name.strip(
+ '/').split('/')
links += [
SitemapEntry('Clone of'),
SitemapEntry('%s / %s' %
- (repo_path_parts[1], repo_path_parts[-1]),
- self.repo.upstream_repo.name)
- ]
+ (repo_path_parts[1], repo_path_parts[-1]),
+ self.repo.upstream_repo.name)
+ ]
if not c.app.repo.is_empty() and has_access(c.app.repo, 'admin'):
merge_url = c.app.url + 'request_merge'
if getattr(c, 'revision', None):
merge_url = merge_url + '?branch=' + h.urlquote(c.revision)
links.append(SitemapEntry('Request Merge', merge_url,
ui_icon=g.icons['merge'],
- ))
+ ))
pending_upstream_merges = self.repo.pending_upstream_merges()
if pending_upstream_merges:
links.append(SitemapEntry(
- 'Pending Merges',
- self.repo.upstream_repo.name + 'merge-requests/',
- small=pending_upstream_merges))
- ref_url = self.repo.url_for_commit(self.default_branch_name, url_type='ref')
+ 'Pending Merges',
+ self.repo.upstream_repo.name + 'merge-requests/',
+ small=pending_upstream_merges))
+ ref_url = self.repo.url_for_commit(
+ self.default_branch_name, url_type='ref')
branches = self.repo.get_branches()
if branches:
links.append(SitemapEntry('Branches'))
@@ -151,28 +161,28 @@ class RepositoryApp(Application):
max_branches = 10
for branch in branches[:max_branches]:
links.append(SitemapEntry(
- branch.name,
- quote(self.repo.url_for_commit(branch.name) + 'tree/')))
+ branch.name,
+ quote(self.repo.url_for_commit(branch.name) + 'tree/')))
if len(branches) > max_branches:
links.append(
SitemapEntry(
'More Branches',
ref_url + 'branches/',
- ))
+ ))
tags = self.repo.get_tags()
if tags:
links.append(SitemapEntry('Tags'))
max_tags = 10
for b in tags[:max_tags]:
links.append(SitemapEntry(
- b.name,
- quote(self.repo.url_for_commit(b.name) + 'tree/')))
+ b.name,
+ quote(self.repo.url_for_commit(b.name) + 'tree/')))
if len(tags) > max_tags:
links.append(
SitemapEntry(
'More Tags',
ref_url + 'tags/',
- ))
+ ))
return links
def install(self, project):
@@ -191,11 +201,12 @@ class RepositoryApp(Application):
M.ACE.allow(role_developer, 'moderate'),
M.ACE.allow(role_admin, 'configure'),
M.ACE.allow(role_admin, 'admin'),
- ]
+ ]
def uninstall(self, project):
allura.tasks.repo_tasks.uninstall.post()
+
class RepoAdminController(DefaultAdminController):
def __init__(self, app):
@@ -224,7 +235,8 @@ class RepoAdminController(DefaultAdminController):
@expose()
@require_post()
def set_extensions(self, **post_data):
- self.repo.additional_viewable_extensions = post_data['additional_viewable_extensions']
+ self.repo.additional_viewable_extensions = post_data[
+ 'additional_viewable_extensions']
@without_trailing_slash
@expose('jinja:allura:templates/repo/default_branch.html')
@@ -235,4 +247,3 @@ class RepoAdminController(DefaultAdminController):
else:
return dict(app=self.app,
default_branch_name=self.app.default_branch_name)
-
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/rest_api.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/rest_api.py b/Allura/allura/lib/rest_api.py
index f0d74f8..dcc0d7d 100644
--- a/Allura/allura/lib/rest_api.py
+++ b/Allura/allura/lib/rest_api.py
@@ -30,6 +30,7 @@ from formencode import variabledecode
log = logging.getLogger(__name__)
+
class RestClient(object):
def __init__(self, api_key, secret_key, base_uri,
@@ -49,13 +50,18 @@ class RestClient(object):
self._opener = urllib2.build_opener(redirect_handler)
def sign_request(self, path, params):
- if hasattr(params, 'items'): params = params.items()
+ if hasattr(params, 'items'):
+ params = params.items()
has_api_key = has_api_timestamp = has_api_signature = False
- for k,v in params:
- if k == 'api_key': has_api_key = True
- if k == 'api_timestamp': has_api_timestamp = True
- if k == 'api_signature': has_api_signature = True
- if not has_api_key: params.append(('api_key', self._api_key))
+ for k, v in params:
+ if k == 'api_key':
+ has_api_key = True
+ if k == 'api_timestamp':
+ has_api_timestamp = True
+ if k == 'api_signature':
+ has_api_signature = True
+ if not has_api_key:
+ params.append(('api_key', self._api_key))
if not has_api_timestamp:
params.append(('api_timestamp', datetime.utcnow().isoformat()))
if not has_api_signature:
@@ -71,15 +77,17 @@ class RestClient(object):
def _redirect_handler_class(self):
client = self
+
class RedirectHandler(urllib2.HTTPRedirectHandler):
+
def redirect_request(self, req, fp, code, msg, headers, newurl):
m = req.get_method()
if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
- or code in (301, 302, 303) and m == "POST"):
+ or code in (301, 302, 303) and m == "POST"):
newurl = newurl.replace(' ', '%20')
- newheaders = dict((k,v) for k,v in req.headers.items()
+ newheaders = dict((k, v) for k, v in req.headers.items()
if k.lower() not in ("content-length", "content-type")
- )
+ )
result = urlparse(newurl)
log.debug('Redirect to %s' % result.path)
return client.Request(
@@ -88,39 +96,51 @@ class RestClient(object):
origin_req_host=req.get_origin_req_host(),
unverifiable=True)
else:
- raise urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
+ raise urllib2.HTTPError(
+ req.get_full_url(), code, msg, headers, fp)
return RedirectHandler
def _request_class(self):
client = self
+
class Request(urllib2.Request):
+
def __init__(self, method, path, params=None, **kwargs):
- if params is None: params = {}
- params = variabledecode.variable_encode(params, add_repetitions=False)
+ if params is None:
+ params = {}
+ params = variabledecode.variable_encode(
+ params, add_repetitions=False)
params = client.sign_request(path, params)
self._method = method.upper()
if self._method == 'GET':
- url = urljoin(client.base_uri, path) + '?' + urlencode(params)
- data=None
+ url = urljoin(client.base_uri, path) + \
+ '?' + urlencode(params)
+ data = None
else:
url = urljoin(client.base_uri, path)
- data=urlencode(params)
+ data = urlencode(params)
urllib2.Request.__init__(self, url, data=data, **kwargs)
+
def get_method(self):
return self._method
return Request
+
def generate_smart_str(params):
- if isinstance(params, dict): iterparams = params.iteritems()
- else: iterparams = iter(params)
+ if isinstance(params, dict):
+ iterparams = params.iteritems()
+ else:
+ iterparams = iter(params)
for key, value in iterparams:
- if value is None: continue
+ if value is None:
+ continue
if isinstance(value, (list, tuple)):
for item in value:
yield smart_str(key), smart_str(item)
else:
yield smart_str(key), smart_str(value)
+
def urlencode(params):
"""
A version of Python's urllib.urlencode() function that can operate on
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/search.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/search.py b/Allura/allura/lib/search.py
index d43af9c..419a473 100644
--- a/Allura/allura/lib/search.py
+++ b/Allura/allura/lib/search.py
@@ -34,10 +34,13 @@ from .markdown_extensions import ForgeExtension
log = getLogger(__name__)
+
def solarize(obj):
- if obj is None: return None
+ if obj is None:
+ return None
doc = obj.index()
- if doc is None: return None
+ if doc is None:
+ return None
# if index() returned doc without text, assume empty text
if not doc.get('text'):
doc['text'] = ''
@@ -48,16 +51,19 @@ def solarize(obj):
doc['text'] = jinja2.Markup.escape(text).striptags()
return doc
+
class SearchError(SolrError):
pass
+
def inject_user(q, user=None):
'''Replace $USER with current user's name.'''
if user is None:
user = c.user
return q.replace('$USER', '"%s"' % user.username) if q else q
-def search(q,short_timeout=False,ignore_errors=True,**kw):
+
+def search(q, short_timeout=False, ignore_errors=True, **kw):
q = inject_user(q)
try:
if short_timeout:
@@ -68,7 +74,9 @@ def search(q,short_timeout=False,ignore_errors=True,**kw):
log.exception('Error in solr search')
if not ignore_errors:
match = re.search(r'<pre>(.*)</pre>', str(e))
- raise SearchError('Error running search query: %s' % (match.group(1) if match else e))
+ raise SearchError('Error running search query: %s' %
+ (match.group(1) if match else e))
+
def search_artifact(atype, q, history=False, rows=10, short_timeout=False, **kw):
"""Performs SOLR search.
@@ -77,14 +85,15 @@ def search_artifact(atype, q, history=False, rows=10, short_timeout=False, **kw)
"""
# first, grab an artifact and get the fields that it indexes
a = atype.query.find().first()
- if a is None: return # if there are no instance of atype, we won't find anything
+ if a is None:
+ return # if there are no instance of atype, we won't find anything
fields = a.index()
# Now, we'll translate all the fld:
q = atype.translate_query(q, fields)
fq = [
'type_s:%s' % fields['type_s'],
'project_id_s:%s' % c.project._id,
- 'mount_point_s:%s' % c.app.config.options.mount_point ]
+ 'mount_point_s:%s' % c.app.config.options.mount_point]
if not history:
fq.append('is_history_b:False')
return search(q, fq=fq, rows=rows, short_timeout=short_timeout, ignore_errors=False, **kw)
@@ -97,8 +106,10 @@ def search_app(q='', fq=None, app=True, **kw):
"""
history = kw.pop('history', None)
if app and kw.pop('project', False):
- # Used from app's search controller. If `project` is True, redirect to 'entire project search' page
- redirect(c.project.url() + 'search/?' + urlencode(dict(q=q, history=history)))
+ # Used from app's search controller. If `project` is True, redirect to
+ # 'entire project search' page
+ redirect(c.project.url() + 'search/?' +
+ urlencode(dict(q=q, history=history)))
search_comments = kw.pop('search_comments', None)
limit = kw.pop('limit', None)
page = kw.pop('page', 0)
@@ -122,10 +133,11 @@ def search_app(q='', fq=None, app=True, **kw):
allowed_types += ['Post']
if app:
fq = [
- 'project_id_s:%s' % c.project._id,
+ 'project_id_s:%s' % c.project._id,
'mount_point_s:%s' % c.app.config.options.mount_point,
'-deleted_b:true',
- 'type_s:(%s)' % ' OR '.join(['"%s"' % t for t in allowed_types])
+ 'type_s:(%s)' % ' OR '.join(
+ ['"%s"' % t for t in allowed_types])
] + fq
search_params = {
'qt': 'dismax',
@@ -138,7 +150,7 @@ def search_app(q='', fq=None, app=True, **kw):
'sort': sort,
}
if not history:
- search_params['fq'].append('is_history_b:False')
+ search_params['fq'].append('is_history_b:False')
if parser == 'standard':
search_params.pop('qt', None)
search_params.pop('qf', None)
@@ -152,11 +164,14 @@ def search_app(q='', fq=None, app=True, **kw):
if results:
count = results.hits
matches = results.highlighting
+
def historize_urls(doc):
if doc.get('type_s', '').endswith(' Snapshot'):
if doc.get('url_s'):
- doc['url_s'] = doc['url_s'] + '?version=%s' % doc.get('version_i')
+ doc['url_s'] = doc['url_s'] + \
+ '?version=%s' % doc.get('version_i')
return doc
+
def add_matches(doc):
m = matches.get(doc['id'], {})
title = h.get_first(m, 'title')
@@ -172,6 +187,7 @@ def search_app(q='', fq=None, app=True, **kw):
doc['title_match'] = title
doc['text_match'] = text or h.get_first(doc, 'text')
return doc
+
def paginate_comment_urls(doc):
if doc.get('type_s', '') == 'Post':
aref = ArtifactReference.query.get(_id=doc.get('id'))
@@ -211,4 +227,4 @@ def find_shortlinks(text):
output_format='html4')
md.convert(text)
link_index = md.treeprocessors['links'].alinks
- return [ link for link in link_index if link is not None]
+ return [link for link in link_index if link is not None]
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/security.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 49d6415..a0d6c1e 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -31,7 +31,9 @@ from allura.lib.utils import TruthyCallable
log = logging.getLogger(__name__)
+
class Credentials(object):
+
'''
Role graph logic & caching
'''
@@ -58,7 +60,8 @@ class Credentials(object):
def clear_user(self, user_id, project_id=None):
if project_id == '*':
- to_remove = [(uid, pid) for uid, pid in self.users if uid == user_id]
+ to_remove = [(uid, pid)
+ for uid, pid in self.users if uid == user_id]
else:
to_remove = [(user_id, project_id)]
for uid, pid in to_remove:
@@ -68,8 +71,10 @@ class Credentials(object):
def load_user_roles(self, user_id, *project_ids):
'''Load the credentials with all user roles for a set of projects'''
# Don't reload roles
- project_ids = [ pid for pid in project_ids if self.users.get((user_id, pid)) is None ]
- if not project_ids: return
+ project_ids = [
+ pid for pid in project_ids if self.users.get((user_id, pid)) is None]
+ if not project_ids:
+ return
if user_id is None:
q = self.project_role.find({
'user_id': None,
@@ -94,8 +99,10 @@ class Credentials(object):
def load_project_roles(self, *project_ids):
'''Load the credentials with all user roles for a set of projects'''
# Don't reload roles
- project_ids = [ pid for pid in project_ids if self.projects.get(pid) is None ]
- if not project_ids: return
+ project_ids = [
+ pid for pid in project_ids if self.projects.get(pid) is None]
+ if not project_ids:
+ return
q = self.project_role.find({
'project_id': {'$in': project_ids}})
roles_by_project = dict((pid, []) for pid in project_ids)
@@ -134,17 +141,18 @@ class Credentials(object):
def user_has_any_role(self, user_id, project_id, role_ids):
user_roles = self.user_roles(user_id=user_id, project_id=project_id)
- return bool(set(role_ids) & user_roles.reaching_ids_set)
+ return bool(set(role_ids) & user_roles.reaching_ids_set)
def users_with_named_role(self, project_id, name):
""" returns in sorted order """
roles = self.project_roles(project_id)
- return sorted(RoleCache(self, roles.find(name=name)).users_that_reach, key=lambda u:u.username)
+ return sorted(RoleCache(self, roles.find(name=name)).users_that_reach, key=lambda u: u.username)
def userids_with_named_role(self, project_id, name):
roles = self.project_roles(project_id)
return RoleCache(self, roles.find(name=name)).userids_that_reach
+
class RoleCache(object):
def __init__(self, cred, q):
@@ -153,19 +161,23 @@ class RoleCache(object):
def find(self, **kw):
tests = kw.items()
+
def _iter():
for r in self:
- for k,v in tests:
+ for k, v in tests:
val = r.get(k)
if callable(v):
- if not v(val): break
- elif v != val: break
+ if not v(val):
+ break
+ elif v != val:
+ break
else:
yield r
return RoleCache(self.cred, _iter())
def get(self, **kw):
- for x in self.find(**kw): return x
+ for x in self.find(**kw):
+ return x
return None
def __iter__(self):
@@ -199,10 +211,12 @@ class RoleCache(object):
to_visit = list(self)
while to_visit:
r = to_visit.pop(0)
- if r['_id'] in visited: continue
+ if r['_id'] in visited:
+ continue
visited.add(r['_id'])
yield r
- pr_rindex = self.cred.project_roles(r['project_id']).reverse_index
+ pr_rindex = self.cred.project_roles(
+ r['project_id']).reverse_index
to_visit += pr_rindex[r['_id']]
return RoleCache(self.cred, _iter())
@@ -214,7 +228,7 @@ class RoleCache(object):
@LazyProperty
def userids_that_reach(self):
- return [ r['user_id'] for r in self.roles_that_reach ]
+ return [r['user_id'] for r in self.roles_that_reach]
@LazyProperty
def reaching_roles(self):
@@ -223,7 +237,8 @@ class RoleCache(object):
visited = set()
while to_visit:
(rid, role) = to_visit.pop()
- if rid in visited: continue
+ if rid in visited:
+ continue
yield role
pr_index = self.cred.project_roles(role['project_id']).index
if rid in pr_index:
@@ -234,12 +249,13 @@ class RoleCache(object):
@LazyProperty
def reaching_ids(self):
- return [ r['_id'] for r in self.reaching_roles ]
+ return [r['_id'] for r in self.reaching_roles]
@LazyProperty
def reaching_ids_set(self):
return set(self.reaching_ids)
+
def has_access(obj, permission, user=None, project=None):
'''Return whether the given user has the permission name on the given object.
@@ -280,11 +296,13 @@ def has_access(obj, permission, user=None, project=None):
3. Otherwise, DENY access to the resource.
'''
from allura import model as M
+
def predicate(obj=obj, user=user, project=project, roles=None):
if obj is None:
return False
if roles is None:
- if user is None: user = c.user
+ if user is None:
+ user = c.user
assert user, 'c.user should always be at least M.User.anonymous()'
cred = Credentials.get()
if project is None:
@@ -298,12 +316,13 @@ def has_access(obj, permission, user=None, project=None):
else:
project = getattr(obj, 'project', None) or c.project
project = project.root_project
- roles = cred.user_roles(user_id=user._id, project_id=project._id).reaching_ids
+ roles = cred.user_roles(
+ user_id=user._id, project_id=project._id).reaching_ids
# TODO: move deny logic into loop below; see ticket [#6715]
if user != M.User.anonymous():
user_roles = Credentials.get().user_roles(user_id=user._id,
- project_id=project.root_project._id)
+ project_id=project.root_project._id)
for r in user_roles:
deny_user = M.ACE.deny(r['_id'], permission)
if M.ACL.contains(deny_user, obj.acl):
@@ -337,6 +356,7 @@ def has_access(obj, permission, user=None, project=None):
return result
return TruthyCallable(predicate)
+
def all_allowed(obj, user_or_role=None, project=None):
'''
List all the permission names that a given user or named role
@@ -380,7 +400,8 @@ def all_allowed(obj, user_or_role=None, project=None):
roles += [anon] # auth inherits from anon
else:
roles += [auth, anon] # named group or user inherits from auth + anon
- role_ids = RoleCache(Credentials.get(), roles).reaching_ids # match rules applicable to us
+ # match rules applicable to us
+ role_ids = RoleCache(Credentials.get(), roles).reaching_ids
perms = set()
denied = defaultdict(set)
while obj: # traverse parent contexts
@@ -395,13 +416,15 @@ def all_allowed(obj, user_or_role=None, project=None):
else:
# explicit DENY overrides any ALLOW for this permission
# for this role_id in this ACL or parent(s) (but an ALLOW
- # for a different role could still grant this permission)
+ # for a different role could still grant this
+ # permission)
denied[role_id].add(ace.permission)
obj = obj.parent_security_context()
if M.ALL_PERMISSIONS in perms:
return set([M.ALL_PERMISSIONS])
return perms
+
def require(predicate, message=None):
'''
Example: require(has_access(c.app, 'read'))
@@ -412,7 +435,8 @@ def require(predicate, message=None):
'''
from allura import model as M
- if predicate(): return
+ if predicate():
+ return
if not message:
message = """You don't have permission to do that.
You must ask a project administrator for rights to perform this task.
@@ -423,12 +447,15 @@ def require(predicate, message=None):
else:
raise exc.HTTPUnauthorized()
+
def require_access(obj, permission, **kwargs):
if obj is not None:
predicate = has_access(obj, permission, **kwargs)
return require(predicate, message='%s access required' % permission.capitalize())
else:
- raise exc.HTTPForbidden(detail="Could not verify permissions for this page.")
+ raise exc.HTTPForbidden(
+ detail="Could not verify permissions for this page.")
+
def require_authenticated():
'''
@@ -438,12 +465,15 @@ def require_authenticated():
if c.user == M.User.anonymous():
raise exc.HTTPUnauthorized()
+
def simple_grant(acl, role_id, permission):
from allura.model.types import ACE
for ace in acl:
- if ace.role_id == role_id and ace.permission == permission: return
+ if ace.role_id == role_id and ace.permission == permission:
+ return
acl.append(ACE.allow(role_id, permission))
+
def simple_revoke(acl, role_id, permission):
remove = []
for i, ace in enumerate(acl):
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/solr.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/solr.py b/Allura/allura/lib/solr.py
index 4fa0c77..22adbe6 100644
--- a/Allura/allura/lib/solr.py
+++ b/Allura/allura/lib/solr.py
@@ -37,6 +37,7 @@ def make_solr_from_config(push_servers, query_server=None, **kwargs):
class Solr(object):
+
"""Solr interface that pushes updates to multiple solr instances.
`push_servers`: list of servers to push to.
@@ -88,6 +89,7 @@ class Solr(object):
class MockSOLR(object):
class MockHits(list):
+
@property
def hits(self):
return len(self)
@@ -113,7 +115,8 @@ class MockSOLR(object):
# Parse query
preds = []
q_parts = shlex.split(q)
- if fq: q_parts += fq
+ if fq:
+ q_parts += fq
for part in q_parts:
if part == '&&':
continue
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/spam/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/__init__.py b/Allura/allura/lib/spam/__init__.py
index cfb5c41..4bf3917 100644
--- a/Allura/allura/lib/spam/__init__.py
+++ b/Allura/allura/lib/spam/__init__.py
@@ -23,7 +23,9 @@ log = logging.getLogger(__name__)
class SpamFilter(object):
+
"""Defines the spam checker interface and provides a default no-op impl."""
+
def __init__(self, config):
pass
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/spam/akismetfilter.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/akismetfilter.py b/Allura/allura/lib/spam/akismetfilter.py
index 7020632..48f24cc 100644
--- a/Allura/allura/lib/spam/akismetfilter.py
+++ b/Allura/allura/lib/spam/akismetfilter.py
@@ -34,6 +34,7 @@ log = logging.getLogger(__name__)
class AkismetSpamFilter(SpamFilter):
+
"""Spam checking implementation via Akismet service.
To enable Akismet spam filtering in your Allura instance, first
@@ -47,10 +48,12 @@ class AkismetSpamFilter(SpamFilter):
spam.method = akismet
spam.key = <your Akismet key here>
"""
+
def __init__(self, config):
if not AKISMET_AVAILABLE:
raise ImportError('akismet not available')
- self.service = akismet.Akismet(config.get('spam.key'), config.get('base_url'))
+ self.service = akismet.Akismet(
+ config.get('spam.key'), config.get('base_url'))
self.service.verify_key()
def get_data(self, text, artifact=None, user=None, content_type='comment', **kw):
@@ -62,7 +65,8 @@ class AkismetSpamFilter(SpamFilter):
user = user or c.user
if user:
kw['comment_author'] = user.display_name or user.username
- kw['comment_author_email'] = user.email_addresses[0] if user.email_addresses else ''
+ kw['comment_author_email'] = user.email_addresses[
+ 0] if user.email_addresses else ''
user_ip = request.headers.get('X_FORWARDED_FOR', request.remote_addr)
kw['user_ip'] = user_ip.split(',')[0].strip()
kw['user_agent'] = request.headers.get('USER_AGENT')
@@ -93,8 +97,8 @@ class AkismetSpamFilter(SpamFilter):
def submit_ham(self, text, artifact=None, user=None, content_type='comment'):
self.service.submit_ham(text,
- data=self.get_data(text=text,
- artifact=artifact,
- user=user,
- content_type=content_type),
- build_data=False)
+ data=self.get_data(text=text,
+ artifact=artifact,
+ user=user,
+ content_type=content_type),
+ build_data=False)
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/spam/mollomfilter.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/mollomfilter.py b/Allura/allura/lib/spam/mollomfilter.py
index b00f978..d76ff59 100644
--- a/Allura/allura/lib/spam/mollomfilter.py
+++ b/Allura/allura/lib/spam/mollomfilter.py
@@ -34,6 +34,7 @@ log = logging.getLogger(__name__)
class MollomSpamFilter(SpamFilter):
+
"""Spam checking implementation via Mollom service.
To enable Mollom spam filtering in your Allura instance, first
@@ -48,6 +49,7 @@ class MollomSpamFilter(SpamFilter):
spam.public_key = <your Mollom public key here>
spam.private_key = <your Mollom private key here>
"""
+
def __init__(self, config):
if not MOLLOM_AVAILABLE:
raise ImportError('Mollom not available')
@@ -71,7 +73,8 @@ class MollomSpamFilter(SpamFilter):
user = user or c.user
if user:
kw['authorName'] = user.display_name or user.username
- kw['authorMail'] = user.email_addresses[0] if user.email_addresses else ''
+ kw['authorMail'] = user.email_addresses[
+ 0] if user.email_addresses else ''
user_ip = request.headers.get('X_FORWARDED_FOR', request.remote_addr)
kw['authorIP'] = user_ip.split(',')[0].strip()
# kw will be urlencoded, need to utf8-encode
@@ -79,7 +82,7 @@ class MollomSpamFilter(SpamFilter):
kw[k] = h.really_unicode(v).encode('utf8')
cc = self.service.checkContent(**kw)
res = cc['spam'] == 2
- artifact.spam_check_id = cc.get('session_id','')
+ artifact.spam_check_id = cc.get('session_id', '')
log.info("spam=%s (mollom): %s" % (str(res), log_msg))
return res
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/stats.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/stats.py b/Allura/allura/lib/stats.py
index ef6dc01..9864e30 100644
--- a/Allura/allura/lib/stats.py
+++ b/Allura/allura/lib/stats.py
@@ -20,6 +20,7 @@ from time import time
from contextlib import contextmanager
from pylons import request
+
class StatsRecord(object):
def __init__(self, request, active):
@@ -34,8 +35,8 @@ class StatsRecord(object):
def __repr__(self):
stats = ' '.join(
- ('%s=%.0fms' % (k,v*1000))
- for k,v in sorted(self.timers.iteritems()))
+ ('%s=%.0fms' % (k, v * 1000))
+ for k, v in sorted(self.timers.iteritems()))
return '%s: %s' % (self.url, stats)
def asdict(self):
@@ -53,12 +54,14 @@ class StatsRecord(object):
yield
finally:
end = time()
- self.timers[name] += end-begin
+ self.timers[name] += end - begin
self._now_timing.remove(name)
else:
yield
+
class timing(object):
+
'''Decorator to time a method call'''
def __init__(self, timer):
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index d35b17e..95622c9 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -74,6 +74,7 @@ def guess_mime_type(filename):
class ConfigProxy(object):
+
'''Wrapper for loading config values at module-scope so we don't
have problems when a module is imported before tg.config is initialized
'''
@@ -92,6 +93,7 @@ class ConfigProxy(object):
class lazy_logger(object):
+
'''Lazy instatiation of a logger, to ensure that it does not get
created before logging is configured (which would make it disabled)'''
@@ -103,15 +105,18 @@ class lazy_logger(object):
return logging.getLogger(self._name)
def __getattr__(self, name):
- if name.startswith('_'): raise AttributeError, name
+ if name.startswith('_'):
+ raise AttributeError, name
return getattr(self._logger, name)
+
class TimedRotatingHandler(logging.handlers.BaseRotatingHandler):
def __init__(self, strftime_pattern):
self.pattern = strftime_pattern
self.last_filename = self.current_filename()
- logging.handlers.BaseRotatingHandler.__init__(self, self.last_filename, 'a')
+ logging.handlers.BaseRotatingHandler.__init__(
+ self, self.last_filename, 'a')
def current_filename(self):
return os.path.abspath(datetime.datetime.utcnow().strftime(self.pattern))
@@ -128,9 +133,11 @@ class TimedRotatingHandler(logging.handlers.BaseRotatingHandler):
else:
self.stream = open(self.baseFilename, 'w')
+
class StatsHandler(TimedRotatingHandler):
- fields=('action', 'action_type', 'tool_type', 'tool_mount', 'project', 'neighborhood',
- 'username', 'url', 'ip_address')
+ fields = (
+ 'action', 'action_type', 'tool_type', 'tool_mount', 'project', 'neighborhood',
+ 'username', 'url', 'ip_address')
def __init__(self,
strftime_pattern,
@@ -151,13 +158,14 @@ class StatsHandler(TimedRotatingHandler):
kwpairs[name] = getattr(record, name, None)
kwpairs.update(getattr(record, 'kwpairs', {}))
record.kwpairs = ','.join(
- '%s=%s' % (k,v) for k,v in sorted(kwpairs.iteritems())
+ '%s=%s' % (k, v) for k, v in sorted(kwpairs.iteritems())
if v is not None)
- record.exc_info = None # Never put tracebacks in the rtstats log
+ record.exc_info = None # Never put tracebacks in the rtstats log
TimedRotatingHandler.emit(self, record)
class CustomWatchedFileHandler(logging.handlers.WatchedFileHandler):
+
"""Custom log handler for Allura"""
def format(self, record):
@@ -179,7 +187,8 @@ def chunked_find(cls, query=None, pagesize=1024, sort_key='_id', sort_dir=1):
Pass an indexed sort_key for efficient queries. Default _id should work
in most cases.
'''
- if query is None: query = {}
+ if query is None:
+ query = {}
page = 0
max_id = None
while True:
@@ -200,6 +209,7 @@ def chunked_find(cls, query=None, pagesize=1024, sort_key='_id', sort_dir=1):
yield results
page += 1
+
def lsub_utf8(s, n):
'''Useful for returning n bytes of a UTF-8 string, rather than characters'''
while len(s) > n:
@@ -209,22 +219,26 @@ def lsub_utf8(s, n):
return s[:k]
return s
+
def chunked_list(l, n):
""" Yield successive n-sized chunks from l.
"""
for i in xrange(0, len(l), n):
- yield l[i:i+n]
+ yield l[i:i + n]
+
def chunked_iter(iterable, max_size):
'''return iterable 'chunks' from the iterable of max size max_size'''
eiter = enumerate(iterable)
- keyfunc = lambda (i,x): i//max_size
+ keyfunc = lambda (i, x): i // max_size
for _, chunk in groupby(eiter, keyfunc):
- yield (x for i,x in chunk)
+ yield (x for i, x in chunk)
+
class AntiSpam(object):
+
'''Helper class for bot-protecting forms'''
- honey_field_template=string.Template('''<p class="$honey_class">
+ honey_field_template = string.Template('''<p class="$honey_class">
<label for="$fld_id">You seem to have CSS turned off.
Please don't fill out this field.</label><br>
<input id="$fld_id" name="$fld_name" type="text"><br></p>''')
@@ -244,7 +258,7 @@ class AntiSpam(object):
self.timestamp = int(self.timestamp_text)
self.spinner = self._unwrap(self.spinner_text)
self.spinner_ord = map(ord, self.spinner)
- self.random_padding = [ random.randint(0,255) for x in self.spinner ]
+ self.random_padding = [random.randint(0, 255) for x in self.spinner]
self.honey_class = self.enc(self.spinner_text, css_safe=True)
# The counter is to ensure that multiple forms in the same page
@@ -289,10 +303,10 @@ class AntiSpam(object):
'''
# Plain starts with its length, includes the ordinals for its
# characters, and is padded with random data
- plain = ([ len(plain) ]
+ plain = ([len(plain)]
+ map(ord, plain)
+ self.random_padding[:len(self.spinner_ord) - len(plain) - 1])
- enc = ''.join(chr(p^s) for p, s in zip(plain, self.spinner_ord))
+ enc = ''.join(chr(p ^ s) for p, s in zip(plain, self.spinner_ord))
enc = self._wrap(enc)
if css_safe:
enc = ''.join(ch for ch in enc if ch.isalpha())
@@ -301,8 +315,8 @@ class AntiSpam(object):
def dec(self, enc):
enc = self._unwrap(enc)
enc = list(map(ord, enc))
- plain = [e^s for e,s in zip(enc, self.spinner_ord)]
- plain = plain[1:1+plain[0]]
+ plain = [e ^ s for e, s in zip(enc, self.spinner_ord)]
+ plain = plain[1:1 + plain[0]]
plain = ''.join(map(chr, plain))
return plain
@@ -313,15 +327,17 @@ class AntiSpam(object):
fld_name = self.enc('honey%d' % (fldno))
fld_id = self.enc('honey%d%d' % (self.counter, fldno))
yield literal(self.honey_field_template.substitute(
- honey_class=self.honey_class,
- fld_id=fld_id,
- fld_name=fld_name))
+ honey_class=self.honey_class,
+ fld_id=fld_id,
+ fld_name=fld_name))
self.counter += 1
def make_spinner(self, timestamp=None):
- if timestamp is None: timestamp = self.timestamp
+ if timestamp is None:
+ timestamp = self.timestamp
try:
- client_ip = self.request.headers.get('X_FORWARDED_FOR', self.request.remote_addr)
+ client_ip = self.request.headers.get(
+ 'X_FORWARDED_FOR', self.request.remote_addr)
client_ip = client_ip.split(',')[0].strip()
except (TypeError, AttributeError), err:
client_ip = '127.0.0.1'
@@ -331,17 +347,20 @@ class AntiSpam(object):
@classmethod
def validate_request(cls, request=None, now=None, params=None):
- if request is None: request = pylons.request
- if params is None: params = request.params
+ if request is None:
+ request = pylons.request
+ if params is None:
+ params = request.params
new_params = dict(params)
if not request.method == 'GET':
new_params.pop('timestamp', None)
new_params.pop('spinner', None)
obj = cls(request)
- if now is None: now = time.time()
+ if now is None:
+ now = time.time()
if obj.timestamp > now + 5:
raise ValueError, 'Post from the future'
- if now - obj.timestamp > 24*60*60:
+ if now - obj.timestamp > 24 * 60 * 60:
raise ValueError, 'Post from the distant past'
if obj.spinner != obj.make_spinner(obj.timestamp):
raise ValueError, 'Bad spinner value'
@@ -365,21 +384,27 @@ class AntiSpam(object):
raise Invalid(error_msg, params, None)
return before_validate(antispam_hook)
+
class TruthyCallable(object):
+
'''
Wraps a callable to make it truthy in a boolean context.
Assumes the callable returns a truthy value and can be called with no args.
'''
+
def __init__(self, callable):
self.callable = callable
+
def __call__(self, *args, **kw):
return self.callable(*args, **kw)
+
def __nonzero__(self):
return self.callable()
class TransformedDict(collections.MutableMapping):
+
"""
A dictionary which applies an arbitrary
key-altering function before accessing the keys.
@@ -389,7 +414,7 @@ class TransformedDict(collections.MutableMapping):
def __init__(self, *args, **kwargs):
self.store = dict()
- self.update(dict(*args, **kwargs)) # use the free update to set keys
+ self.update(dict(*args, **kwargs)) # use the free update to set keys
def __getitem__(self, key):
return self.store[self.__keytransform__(key)]
@@ -416,17 +441,20 @@ class CaseInsensitiveDict(TransformedDict):
return key.lower()
-def postmortem_hook(etype, value, tb): # pragma no cover
- import sys, pdb, traceback
+def postmortem_hook(etype, value, tb): # pragma no cover
+ import sys
+ import pdb
+ import traceback
try:
- from IPython.ipapi import make_session; make_session()
+ from IPython.ipapi import make_session
+ make_session()
from IPython.Debugger import Pdb
sys.stderr.write('Entering post-mortem IPDB shell\n')
p = Pdb(color_scheme='Linux')
p.reset()
p.setup(None, tb)
p.print_stack_trace()
- sys.stderr.write('%s: %s\n' % ( etype, value))
+ sys.stderr.write('%s: %s\n' % (etype, value))
p.cmdloop()
p.forget()
# p.interaction(None, tb)
@@ -435,7 +463,9 @@ def postmortem_hook(etype, value, tb): # pragma no cover
traceback.print_exception(etype, value, tb)
pdb.post_mortem(tb)
+
class LineAnchorCodeHtmlFormatter(HtmlFormatter):
+
def _wrap_pre(self, inner):
style = []
if self.prestyles:
@@ -451,6 +481,7 @@ class LineAnchorCodeHtmlFormatter(HtmlFormatter):
num += 1
yield 0, '</pre>'
+
def generate_code_stats(blob):
stats = {'line_count': 0,
'code_size': 0,
@@ -486,7 +517,8 @@ def serve_file(fp, filename, content_type, last_modified=None, cache_expires=Non
etag_cache(etag)
pylons.response.headers['Content-Type'] = ''
pylons.response.content_type = content_type.encode('utf-8')
- pylons.response.cache_expires = cache_expires or asint(tg.config.get('files_expires_header_secs', 60 * 60))
+ pylons.response.cache_expires = cache_expires or asint(
+ tg.config.get('files_expires_header_secs', 60 * 60))
pylons.response.last_modified = last_modified
if size:
pylons.response.content_length = size
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/validators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/validators.py b/Allura/allura/lib/validators.py
index 3556fe0..1c0acfd 100644
--- a/Allura/allura/lib/validators.py
+++ b/Allura/allura/lib/validators.py
@@ -23,6 +23,7 @@ from pylons import tmpl_context as c
from . import helpers as h
from datetime import datetime
+
class Ming(fev.FancyValidator):
def __init__(self, cls, **kw):
@@ -41,15 +42,18 @@ class Ming(fev.FancyValidator):
def _from_python(self, value, state):
return value._id
+
class UniqueOAuthApplicationName(fev.UnicodeString):
def _to_python(self, value, state):
from allura import model as M
app = M.OAuthConsumerToken.query.get(name=value)
if app is not None:
- raise fe.Invalid('That name is already taken, please choose another', value, state)
+ raise fe.Invalid(
+ 'That name is already taken, please choose another', value, state)
return value
+
class NullValidator(fev.Validator):
def to_python(self, value, state):
@@ -61,21 +65,25 @@ class NullValidator(fev.Validator):
def validate(self, value, state):
return value
+
class MaxBytesValidator(fev.FancyValidator):
- max=255
+ max = 255
def _to_python(self, value, state):
value = h.really_unicode(value or '').encode('utf-8')
if len(value) > self.max:
- raise fe.Invalid("Please enter a value less than %s bytes long." % self.max, value, state)
+ raise fe.Invalid("Please enter a value less than %s bytes long." %
+ self.max, value, state)
return value
def from_python(self, value, state):
return h.really_unicode(value or '')
+
class MountPointValidator(fev.UnicodeString):
+
def __init__(self, app_class,
- reserved_mount_points=('feed', 'index', 'icon', '_nav.json'), **kw):
+ reserved_mount_points=('feed', 'index', 'icon', '_nav.json'), **kw):
super(self.__class__, self).__init__(**kw)
self.app_class = app_class
self.reserved_mount_points = reserved_mount_points
@@ -86,13 +94,14 @@ class MountPointValidator(fev.UnicodeString):
mount_point = mount_point.lower()
if not App.validate_mount_point(mount_point):
raise fe.Invalid('Mount point "%s" is invalid' % mount_point,
- value, state)
+ value, state)
if mount_point in self.reserved_mount_points:
raise fe.Invalid('Mount point "%s" is reserved' % mount_point,
- value, state)
+ value, state)
if c.project and c.project.app_instance(mount_point) is not None:
- raise fe.Invalid('Mount point "%s" is already in use' % mount_point,
- value, state)
+ raise fe.Invalid(
+ 'Mount point "%s" is already in use' % mount_point,
+ value, state)
return mount_point
def empty_value(self, value):
@@ -104,13 +113,15 @@ class MountPointValidator(fev.UnicodeString):
mount_point = base_mount_point + '-%d' % i
i += 1
+
class TaskValidator(fev.FancyValidator):
+
def _to_python(self, value, state):
try:
mod, func = value.rsplit('.', 1)
except ValueError:
raise fe.Invalid('Invalid task name. Please provide the full '
- 'dotted path to the python callable.', value, state)
+ 'dotted path to the python callable.', value, state)
try:
mod = __import__(mod, fromlist=[str(func)])
except ImportError:
@@ -119,13 +130,16 @@ class TaskValidator(fev.FancyValidator):
try:
task = getattr(mod, func)
except AttributeError:
- raise fe.Invalid('Module has no attribute "%s"' % func, value, state)
+ raise fe.Invalid('Module has no attribute "%s"' %
+ func, value, state)
if not hasattr(task, 'post'):
raise fe.Invalid('"%s" is not a task.' % value, value, state)
return task
+
class UserValidator(fev.FancyValidator):
+
def _to_python(self, value, state):
from allura import model as M
user = M.User.by_username(value)
@@ -133,14 +147,16 @@ class UserValidator(fev.FancyValidator):
raise fe.Invalid('Invalid username', value, state)
return user
+
class PathValidator(fev.FancyValidator):
+
def _to_python(self, value, state):
from allura import model as M
parts = value.strip('/').split('/')
if len(parts) < 2:
raise fe.Invalid("You must specify at least a neighborhood and "
- "project, i.e. '/nbhd/project'", value, state)
+ "project, i.e. '/nbhd/project'", value, state)
elif len(parts) == 2:
nbhd_name, project_name, app_name = parts[0], parts[1], None
elif len(parts) > 2:
@@ -150,24 +166,31 @@ class PathValidator(fev.FancyValidator):
nbhd_url_prefix = '/%s/' % nbhd_name
nbhd = M.Neighborhood.query.get(url_prefix=nbhd_url_prefix)
if not nbhd:
- raise fe.Invalid('Invalid neighborhood: %s' % nbhd_url_prefix, value, state)
+ raise fe.Invalid('Invalid neighborhood: %s' %
+ nbhd_url_prefix, value, state)
- project = M.Project.query.get(shortname=nbhd.shortname_prefix + project_name,
- neighborhood_id=nbhd._id)
+ project = M.Project.query.get(
+ shortname=nbhd.shortname_prefix + project_name,
+ neighborhood_id=nbhd._id)
if not project:
- raise fe.Invalid('Invalid project: %s' % project_name, value, state)
+ raise fe.Invalid('Invalid project: %s' %
+ project_name, value, state)
path_parts['project'] = project
if app_name:
app = project.app_instance(app_name)
if not app:
- raise fe.Invalid('Invalid app mount point: %s' % app_name, value, state)
+ raise fe.Invalid('Invalid app mount point: %s' %
+ app_name, value, state)
path_parts['app'] = app
return path_parts
+
class JsonValidator(fev.FancyValidator):
+
"""Validates a string as JSON and returns the original string"""
+
def _to_python(self, value, state):
try:
json.loads(value)
@@ -175,8 +198,11 @@ class JsonValidator(fev.FancyValidator):
raise fe.Invalid('Invalid JSON: ' + str(e), value, state)
return value
+
class JsonConverter(fev.FancyValidator):
+
"""Deserializes a string to JSON and returns a Python object"""
+
def _to_python(self, value, state):
try:
obj = json.loads(value)
@@ -184,14 +210,19 @@ class JsonConverter(fev.FancyValidator):
raise fe.Invalid('Invalid JSON: ' + str(e), value, state)
return obj
+
class JsonFile(fev.FieldStorageUploadConverter):
+
"""Validates that a file is JSON and returns the deserialized Python object
"""
+
def _to_python(self, value, state):
return JsonConverter.to_python(value.value)
+
class UserMapJsonFile(JsonFile):
+
"""Validates that a JSON file conforms to this format:
{str:str, ...}
@@ -199,6 +230,7 @@ class UserMapJsonFile(JsonFile):
and returns a deserialized or stringified copy of it.
"""
+
def __init__(self, as_string=False):
self.as_string = as_string
@@ -211,8 +243,9 @@ class UserMapJsonFile(JsonFile):
return json.dumps(value) if self.as_string else value
except:
raise fe.Invalid(
- 'User map file must contain mapping of {str:str, ...}',
- value, state)
+ 'User map file must contain mapping of {str:str, ...}',
+ value, state)
+
class CreateTaskSchema(fe.Schema):
task = TaskValidator(not_empty=True, strip=True)
@@ -220,7 +253,9 @@ class CreateTaskSchema(fe.Schema):
user = UserValidator(strip=True, if_missing=None)
path = PathValidator(strip=True, if_missing={}, if_empty={})
+
class DateValidator(fev.FancyValidator):
+
def _to_python(self, value, state):
value = convertDate(value)
if not value:
@@ -229,7 +264,9 @@ class DateValidator(fev.FancyValidator):
value, state)
return value
+
class TimeValidator(fev.FancyValidator):
+
def _to_python(self, value, state):
value = convertTime(value)
if not value:
@@ -238,8 +275,10 @@ class TimeValidator(fev.FancyValidator):
value, state)
return value
+
class OneOfValidator(fev.FancyValidator):
- def __init__(self, validvalues, not_empty = True):
+
+ def __init__(self, validvalues, not_empty=True):
self.validvalues = validvalues
self.not_empty = not_empty
super(OneOfValidator, self).__init__()
@@ -257,12 +296,14 @@ class OneOfValidator(fev.FancyValidator):
allowed = allowed + ', '
allowed = allowed + '"%s"' % v
raise fe.Invalid(
- "Invalid value. The allowed values are %s." %allowed,
+ "Invalid value. The allowed values are %s." % allowed,
value, state)
return value
+
class MapValidator(fev.FancyValidator):
- def __init__(self, mapvalues, not_empty = True):
+
+ def __init__(self, mapvalues, not_empty=True):
self.map = mapvalues
self.not_empty = not_empty
super(MapValidator, self).__init__()
@@ -280,25 +321,27 @@ class MapValidator(fev.FancyValidator):
value, state)
return conv_value
+
def convertDate(datestring):
formats = ['%Y-%m-%d', '%Y.%m.%d', '%Y/%m/%d', '%Y\%m\%d', '%Y %m %d',
'%d-%m-%Y', '%d.%m.%Y', '%d/%m/%Y', '%d\%m\%Y', '%d %m %Y']
for f in formats:
try:
- date = datetime.strptime(datestring, f)
+ date = datetime.strptime(datestring, f)
return date
except:
pass
return None
+
def convertTime(timestring):
formats = ['%H:%M', '%H.%M', '%H %M', '%H,%M']
for f in formats:
try:
- time = datetime.strptime(timestring, f)
- return {'h':time.hour, 'm':time.minute}
+ time = datetime.strptime(timestring, f)
+ return {'h': time.hour, 'm': time.minute}
except:
pass
return None
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/widgets/analytics.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/analytics.py b/Allura/allura/lib/widgets/analytics.py
index cf31715..c2e85c5 100644
--- a/Allura/allura/lib/widgets/analytics.py
+++ b/Allura/allura/lib/widgets/analytics.py
@@ -17,8 +17,9 @@
import ew
+
class GoogleAnalytics(ew.Widget):
- template='jinja:allura:templates/widgets/analytics.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/analytics.html'
+ defaults = dict(
ew.Widget.defaults,
accounts=['UA-XXXXX-X'])
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/widgets/auth_widgets.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/auth_widgets.py b/Allura/allura/lib/widgets/auth_widgets.py
index 93e8826..f6c1a28 100644
--- a/Allura/allura/lib/widgets/auth_widgets.py
+++ b/Allura/allura/lib/widgets/auth_widgets.py
@@ -28,9 +28,10 @@ from .forms import ForgeForm
from allura.lib import plugin
from allura import model as M
+
class LoginForm(ForgeForm):
- submit_text='Login'
- style='wide'
+ submit_text = 'Login'
+ style = 'wide'
@property
def fields(self):
@@ -40,7 +41,8 @@ class LoginForm(ForgeForm):
]
if plugin.AuthenticationProvider.get(request).forgotten_password_process:
# only show link if auth provider has method of recovering password
- fields.append(ew.HTMLField(name='link', text='<a href="forgotten_password">Forgot password?</a>'))
+ fields.append(
+ ew.HTMLField(name='link', text='<a href="forgotten_password">Forgot password?</a>'))
return fields
class hidden_fields(ew_core.NameList):
@@ -49,7 +51,8 @@ class LoginForm(ForgeForm):
@validator
def validate(self, value, state=None):
try:
- value['username'] = plugin.AuthenticationProvider.get(request).login()
+ value['username'] = plugin.AuthenticationProvider.get(
+ request).login()
except exc.HTTPUnauthorized:
msg = 'Invalid login'
raise Invalid(
@@ -60,8 +63,8 @@ class LoginForm(ForgeForm):
class ForgottenPasswordForm(ForgeForm):
- submit_text='Recover password'
- style='wide'
+ submit_text = 'Recover password'
+ style = 'wide'
class fields(ew_core.NameList):
email = ew.TextField(label='Your e-mail')
@@ -73,6 +76,6 @@ class ForgottenPasswordForm(ForgeForm):
user = M.User.by_email_address(email)
if user is None or not email_record.confirmed:
raise Invalid(
- 'Unable to recover password for this email',
- {'email': email}, None)
+ 'Unable to recover password for this email',
+ {'email': email}, None)
return value
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/widgets/discuss.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/discuss.py b/Allura/allura/lib/widgets/discuss.py
index 151a0c2..30dc128 100644
--- a/Allura/allura/lib/widgets/discuss.py
+++ b/Allura/allura/lib/widgets/discuss.py
@@ -27,34 +27,44 @@ from allura.lib.widgets import form_fields as ffw
from allura.lib.widgets import forms as ff
from allura import model as M
+
class NullValidator(fev.FancyValidator):
- perform_validation=True
+ perform_validation = True
+
+ def _to_python(self, value, state):
+ return value
- def _to_python(self, value, state): return value
- def _from_python(self, value, state): return value
+ def _from_python(self, value, state):
+ return value
# Discussion forms
+
+
class ModerateThread(ff.CsrfForm):
- defaults=dict(
+ defaults = dict(
ew.SimpleForm.defaults,
submit_text=None)
+
class buttons(ew_core.NameList):
- delete=ew.SubmitButton(label='Delete Thread')
+ delete = ew.SubmitButton(label='Delete Thread')
+
class ModeratePost(ew.SimpleForm):
- template='jinja:allura:templates/widgets/moderate_post.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/moderate_post.html'
+ defaults = dict(
ew.SimpleForm.defaults,
submit_text=None)
+
class FlagPost(ew.SimpleForm):
- template='jinja:allura:templates/widgets/flag_post.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/flag_post.html'
+ defaults = dict(
ew.SimpleForm.defaults,
submit_text=None)
+
class AttachPost(ff.ForgeForm):
- defaults=dict(
+ defaults = dict(
ff.ForgeForm.defaults,
submit_text='Attach File',
enctype='multipart/form-data')
@@ -62,17 +72,21 @@ class AttachPost(ff.ForgeForm):
@property
def fields(self):
fields = [
- ew.InputField(name='file_info', field_type='file', label='New Attachment')
+ ew.InputField(name='file_info', field_type='file',
+ label='New Attachment')
]
return fields
+
class ModeratePosts(ew.SimpleForm):
- template='jinja:allura:templates/widgets/moderate_posts.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/moderate_posts.html'
+ defaults = dict(
ew.SimpleForm.defaults,
submit_text=None)
+
def resources(self):
- for r in super(ModeratePosts, self).resources(): yield r
+ for r in super(ModeratePosts, self).resources():
+ yield r
yield ew.JSScript('''
(function($){
var tbl = $('form table');
@@ -86,8 +100,9 @@ class ModeratePosts(ew.SimpleForm):
});
}(jQuery));''')
+
class PostFilter(ff.ForgeForm):
- defaults=dict(
+ defaults = dict(
ew.SimpleForm.defaults,
submit_text=None,
method='GET')
@@ -96,22 +111,24 @@ class PostFilter(ff.ForgeForm):
name='page',
validator=fev.Int()),
ew.FieldSet(label='Post Filter', fields=[
- ew.SingleSelectField(
+ ew.SingleSelectField(
name='status',
label='Show posts with status',
options=[
ew.Option(py_value='-', label='Any'),
ew.Option(py_value='spam', label='Spam'),
- ew.Option(py_value='pending', label='Pending moderation'),
+ ew.Option(py_value='pending',
+ label='Pending moderation'),
ew.Option(py_value='ok', label='Ok')],
if_missing='pending'),
- ew.IntField(name='flag',
- label='Show posts with at least "n" flags',
- css_class='text',
- if_missing=0),
- ew.SubmitButton(label='Filter Posts')
- ])
- ]
+ ew.IntField(name='flag',
+ label='Show posts with at least "n" flags',
+ css_class='text',
+ if_missing=0),
+ ew.SubmitButton(label='Filter Posts')
+ ])
+ ]
+
class TagPost(ff.ForgeForm):
@@ -123,15 +140,17 @@ class TagPost(ff.ForgeForm):
result['buttons'] = [submit_button]
return result
- fields=[ffw.LabelEdit(label='Labels',name='labels', className='title')]
+ fields = [ffw.LabelEdit(label='Labels', name='labels', className='title')]
def resources(self):
- for r in ffw.LabelEdit(name='labels').resources(): yield r
+ for r in ffw.LabelEdit(name='labels').resources():
+ yield r
+
class EditPost(ff.ForgeForm):
- template='jinja:allura:templates/widgets/edit_post.html'
- antispam=True
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/edit_post.html'
+ antispam = True
+ defaults = dict(
ff.ForgeForm.defaults,
show_subject=False,
value=None,
@@ -142,12 +161,13 @@ class EditPost(ff.ForgeForm):
fields = ew_core.NameList()
fields.append(ffw.MarkdownEdit(
name='text',
- attrs={'style':'height:7em; width:97%'}))
+ attrs={'style': 'height:7em; width:97%'}))
fields.append(ew.HiddenField(name='forum', if_missing=None))
if ew_core.widget_context.widget:
# we are being displayed
if ew_core.widget_context.render_context.get('show_subject', self.show_subject):
- fields.append(ew.TextField(name='subject',attrs=dict(style="width:97%")))
+ fields.append(
+ ew.TextField(name='subject', attrs=dict(style="width:97%")))
else:
# We are being validated
validator = fev.UnicodeString(not_empty=True, if_missing='')
@@ -156,8 +176,10 @@ class EditPost(ff.ForgeForm):
return fields
def resources(self):
- for r in ew.TextField(name='subject').resources(): yield r
- for r in ffw.MarkdownEdit(name='text').resources(): yield r
+ for r in ew.TextField(name='subject').resources():
+ yield r
+ for r in ffw.MarkdownEdit(name='text').resources():
+ yield r
yield ew.JSScript('''$(document).ready(function () {
$("a.attachment_form_add_button").click(function(evt){
$(this).hide();
@@ -171,48 +193,57 @@ class EditPost(ff.ForgeForm):
});
});''')
+
class NewTopicPost(EditPost):
- template='jinja:allura:templates/widgets/new_topic_post.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/new_topic_post.html'
+ defaults = dict(
EditPost.defaults,
- show_subject = True,
+ show_subject=True,
forums=None)
+
class _ThreadsTable(ew.TableField):
- template='jinja:allura:templates/widgets/threads_table.html'
+ template = 'jinja:allura:templates/widgets/threads_table.html'
+
class fields(ew_core.NameList):
- _id=ew.HiddenField(validator=V.Ming(M.Thread))
- subscription=ew.Checkbox(suppress_label=True)
- subject=ffw.DisplayOnlyField(label='Topic')
- url=ffw.DisplayOnlyField()
- num_replies=ffw.DisplayOnlyField(label='Posts')
- num_views=ffw.DisplayOnlyField(label='Views')
- last_post=ffw.DisplayOnlyField(label='Last Post')
+ _id = ew.HiddenField(validator=V.Ming(M.Thread))
+ subscription = ew.Checkbox(suppress_label=True)
+ subject = ffw.DisplayOnlyField(label='Topic')
+ url = ffw.DisplayOnlyField()
+ num_replies = ffw.DisplayOnlyField(label='Posts')
+ num_views = ffw.DisplayOnlyField(label='Views')
+ last_post = ffw.DisplayOnlyField(label='Last Post')
+
class SubscriptionForm(ew.SimpleForm):
- template='jinja:allura:templates/widgets/subscription_form.html'
- value=None
- threads=None
- show_subject=False
- allow_create_thread=False
- limit=None
- page=0
- count=0
- submit_text='Update Subscriptions'
- params=['value', 'threads', 'limit', 'page', 'count',
- 'show_subject', 'allow_create_thread']
+ template = 'jinja:allura:templates/widgets/subscription_form.html'
+ value = None
+ threads = None
+ show_subject = False
+ allow_create_thread = False
+ limit = None
+ page = 0
+ count = 0
+ submit_text = 'Update Subscriptions'
+ params = ['value', 'threads', 'limit', 'page', 'count',
+ 'show_subject', 'allow_create_thread']
+
class fields(ew_core.NameList):
- page_list=ffw.PageList()
- page_size=ffw.PageSize()
- threads=_ThreadsTable()
+ page_list = ffw.PageList()
+ page_size = ffw.PageSize()
+ threads = _ThreadsTable()
+
def resources(self):
- for r in super(SubscriptionForm, self).resources(): yield r
+ for r in super(SubscriptionForm, self).resources():
+ yield r
yield ew.JSScript('''
$(window).load(function () {
$('tbody').children(':even').addClass('even');
});''')
# Widgets
+
+
class HierWidget(ew_core.Widget):
widgets = {}
@@ -228,37 +259,41 @@ class HierWidget(ew_core.Widget):
for r in w.resources():
yield r
+
class Attachment(ew_core.Widget):
- template='jinja:allura:templates/widgets/attachment.html'
- params=['value', 'post']
- value=None
- post=None
+ template = 'jinja:allura:templates/widgets/attachment.html'
+ params = ['value', 'post']
+ value = None
+ post = None
+
class DiscussionHeader(HierWidget):
- template='jinja:allura:templates/widgets/discussion_header.html'
- params=['value']
- value=None
- widgets=dict(
+ template = 'jinja:allura:templates/widgets/discussion_header.html'
+ params = ['value']
+ value = None
+ widgets = dict(
edit_post=EditPost(submit_text='New Thread'))
+
class ThreadHeader(HierWidget):
- template='jinja:allura:templates/widgets/thread_header.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/thread_header.html'
+ defaults = dict(
HierWidget.defaults,
value=None,
page=None,
limit=None,
count=None,
show_moderate=False)
- widgets=dict(
+ widgets = dict(
page_list=ffw.PageList(),
page_size=ffw.PageSize(),
moderate_thread=ModerateThread(),
tag_post=TagPost())
+
class Post(HierWidget):
- template='jinja:allura:templates/widgets/post_widget.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/post_widget.html'
+ defaults = dict(
HierWidget.defaults,
value=None,
indent=0,
@@ -266,13 +301,15 @@ class Post(HierWidget):
limit=25,
show_subject=False,
suppress_promote=False)
- widgets=dict(
+ widgets = dict(
moderate_post=ModeratePost(),
edit_post=EditPost(submit_text='Post'),
attach_post=AttachPost(submit_text='Attach'),
attachment=Attachment())
+
def resources(self):
- for r in super(Post, self).resources(): yield r
+ for r in super(Post, self).resources():
+ yield r
for w in self.widgets.itervalues():
for r in w.resources():
yield r
@@ -362,9 +399,10 @@ class Post(HierWidget):
}());
''')
+
class PostThread(ew_core.Widget):
- template='jinja:allura:templates/widgets/post_thread.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/post_thread.html'
+ defaults = dict(
ew_core.Widget.defaults,
value=None,
indent=0,
@@ -375,10 +413,11 @@ class PostThread(ew_core.Widget):
parent=None,
children=None)
+
class Thread(HierWidget):
- template='jinja:allura:templates/widgets/thread_widget.html'
- name='thread'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/thread_widget.html'
+ name = 'thread'
+ defaults = dict(
HierWidget.defaults,
value=None,
page=None,
@@ -386,14 +425,16 @@ class Thread(HierWidget):
count=None,
show_subject=False,
new_post_text='+ New Comment')
- widgets=dict(
+ widgets = dict(
page_list=ffw.PageList(),
thread_header=ThreadHeader(),
post_thread=PostThread(),
post=Post(),
edit_post=EditPost(submit_text='Submit'))
+
def resources(self):
- for r in super(Thread, self).resources(): yield r
+ for r in super(Thread, self).resources():
+ yield r
for w in self.widgets.itervalues():
for r in w.resources():
yield r
@@ -441,18 +482,20 @@ class Thread(HierWidget):
});
''')
+
class Discussion(HierWidget):
- template='jinja:allura:templates/widgets/discussion.html'
- defaults=dict(
+ template = 'jinja:allura:templates/widgets/discussion.html'
+ defaults = dict(
HierWidget.defaults,
value=None,
threads=None,
show_subject=False,
allow_create_thread=False)
- widgets=dict(
+ widgets = dict(
discussion_header=DiscussionHeader(),
edit_post=EditPost(submit_text='New Topic'),
subscription_form=SubscriptionForm())
def resources(self):
- for r in super(Discussion, self).resources(): yield r
+ for r in super(Discussion, self).resources():
+ yield r