From ba2c99d5035975d1d64a0a1473f85cc47adaaebc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 12 Nov 2012 15:17:22 -0800 Subject: [PATCH 01/35] bump version --- edbob/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edbob/_version.py b/edbob/_version.py index 3d57e83..7732950 100644 --- a/edbob/_version.py +++ b/edbob/_version.py @@ -1 +1 @@ -__version__ = '0.1a23' +__version__ = '0.1a24' From c79d6de56d25139ddc0b0a4b49c4a3d400689c21 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 12 Nov 2012 22:52:37 -0800 Subject: [PATCH 02/35] fix guest bug in role perms editing --- edbob/db/auth.py | 5 +++-- edbob/pyramid/views/roles.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/edbob/db/auth.py b/edbob/db/auth.py index ad7d72f..31fbb5b 100644 --- a/edbob/db/auth.py +++ b/edbob/db/auth.py @@ -105,7 +105,7 @@ def grant_permission(role, permission, session=None): role.permissions.append(permission) -def has_permission(obj, perm, session=None): +def has_permission(obj, perm, include_guest=True, session=None): """ Checks the given ``obj`` (which may be either a :class:`edbob.User`` or :class:`edbob.Role` instance), and returns a boolean indicating whether or @@ -124,8 +124,9 @@ def has_permission(obj, perm, session=None): if not session: session = object_session(obj) assert session + if include_guest: + roles.append(guest_role(session)) admin = administrator_role(session) - roles.append(guest_role(session)) for role in roles: if role is admin: return True diff --git a/edbob/pyramid/views/roles.py b/edbob/pyramid/views/roles.py index 1281937..2d0aa57 100644 --- a/edbob/pyramid/views/roles.py +++ b/edbob/pyramid/views/roles.py @@ -139,12 +139,12 @@ def PermissionsFieldRenderer(permissions, *args, **kwargs): for group, perms in self.permissions: inner = HTML.tag('p', c=group) for perm, title in perms: - checked = auth.has_permission(role, perm, Session()) + checked = auth.has_permission( + role, perm, include_guest=False, session=Session()) if readonly: span = HTML.tag('span', c="[X]" if checked else "[ ]") inner += HTML.tag('p', class_='perm', c=span + ' ' + title) else: - checked = auth.has_permission(role, perm, Session()) inner += tags.checkbox(self.name + '-' + perm, checked=checked, label=title) html += HTML.tag('div', class_='group', c=inner) From c2339ba124b061da641dd0235e70c5c0d0e7a78f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 12 Nov 2012 22:53:27 -0800 Subject: [PATCH 03/35] exclude guest when editing user roles --- edbob/pyramid/views/users.py | 38 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/edbob/pyramid/views/users.py b/edbob/pyramid/views/users.py index 0ed80c5..a2e201e 100644 --- a/edbob/pyramid/views/users.py +++ b/edbob/pyramid/views/users.py @@ -26,13 +26,14 @@ ``edbob.pyramid.views.users`` -- User Views """ -from webhelpers.html import literal, tags +from webhelpers.html import tags +from webhelpers.html.builder import HTML import formalchemy from formalchemy.fields import SelectFieldRenderer import edbob -from edbob.db.auth import set_user_password +from edbob.db import auth from edbob.pyramid import Session from edbob.pyramid.views import SearchableAlchemyGridView, CrudView @@ -86,22 +87,22 @@ class UsersGrid(SearchableAlchemyGridView): return g -class _RolesFieldRenderer(SelectFieldRenderer): - - def render_readonly(self, **kwargs): - roles = Session.query(edbob.Role) - res = literal('
    ') - for uuid in self.value: - role = roles.get(uuid) - res += literal('
  • %s
  • ' % ( - tags.link_to(role.name, - self.request.route_url('role.read', uuid=role.uuid)))) - res += literal('
') - return res - - def RolesFieldRenderer(request): - return type('RolesFieldRenderer', (_RolesFieldRenderer,), {'request': request}) + + class RolesFieldRenderer(SelectFieldRenderer): + + def render_readonly(self, **kwargs): + roles = Session.query(edbob.Role) + html = '' + for uuid in self.value: + role = roles.get(uuid) + link = tags.link_to( + role.name, request.route_url('role.read', uuid=role.uuid)) + html += HTML.tag('li', c=link) + html = HTML.tag('ul', c=html) + return html + + return RolesFieldRenderer class RolesField(formalchemy.Field): @@ -117,6 +118,7 @@ class RolesField(formalchemy.Field): def get_options(self): q = Session.query(edbob.Role.name, edbob.Role.uuid) + q = q.filter(edbob.Role.uuid != auth.guest_role(Session()).uuid) q = q.order_by(edbob.Role.name) return q.all() @@ -180,7 +182,7 @@ class PasswordField(formalchemy.Field): if not self.is_readonly(): password = self.renderer.deserialize() if password: - set_user_password(self.model, password) + auth.set_user_password(self.model, password) class UserCrud(CrudView): From a249fceed56dc0fc7a64144f1b21922681bf4d06 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 13 Nov 2012 07:55:38 -0800 Subject: [PATCH 04/35] update changelog --- CHANGES.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 7d4a904..a6078f0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,12 @@ +0.1a24 +------ + +- [bug] Fixed bug where creating a new ``Role`` with the UI would use default + permissions of the Guest role. Now the default permissions are empty. + +- [bug] Fixed ``User.roles`` UI so that the Guest role is never shown. + 0.1a23 ------ From c3f58d1b8c6723317d6da853fc372c83796a01f6 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 13 Nov 2012 07:56:54 -0800 Subject: [PATCH 05/35] bump version --- edbob/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edbob/_version.py b/edbob/_version.py index 7732950..3badee1 100644 --- a/edbob/_version.py +++ b/edbob/_version.py @@ -1 +1 @@ -__version__ = '0.1a24' +__version__ = '0.1a25' From 8217b91aa47063aca0ae0e55cf85cace0ea1ba3f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 14 Nov 2012 05:06:31 -0800 Subject: [PATCH 06/35] add sqlerror_tween --- edbob/pyramid/tweens.py | 59 +++++++++++++++++++ .../edbob/+package+/pyramid/__init__.py_tmpl | 26 ++++---- 2 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 edbob/pyramid/tweens.py diff --git a/edbob/pyramid/tweens.py b/edbob/pyramid/tweens.py new file mode 100644 index 0000000..8039efa --- /dev/null +++ b/edbob/pyramid/tweens.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +################################################################################ +# +# edbob -- Pythonic Software Framework +# Copyright © 2010-2012 Lance Edgar +# +# This file is part of edbob. +# +# edbob is free software: you can redistribute it and/or modify it under the +# terms of the GNU Affero General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# edbob is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +# more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with edbob. If not, see . +# +################################################################################ + +""" +``edbob.pyramid.tweens`` -- Tween Factories +""" + +import sqlalchemy.exc + +from transaction.interfaces import TransientError + + +def sqlerror_tween_factory(handler, registry): + """ + Produces a tween which will convert ``sqlalchemy.exc.OperationalError`` + instances (caused by database server restart) into a retryable + ``transaction.interfaces.TransientError`` instance, so that a second + attempt may be made to connect to the database before really giving up. + + .. note:: + This tween alone is not enough to cause the transaction to be retried; + it only marks the error as being *retryable*. If you wish more than one + attempt to be made, you must define the ``tm.attempts`` setting within + your Pyramid app configuration. See `Retrying + `_ + for more information. + """ + + def sqlerror_tween(request): + try: + response = handler(request) + except sqlalchemy.exc.OperationalError, error: + if error.connection_invalidated: + raise TransientError(str(error)) + raise + return response + + return sqlerror_tween diff --git a/edbob/scaffolds/edbob/+package+/pyramid/__init__.py_tmpl b/edbob/scaffolds/edbob/+package+/pyramid/__init__.py_tmpl index efdc27d..155371b 100644 --- a/edbob/scaffolds/edbob/+package+/pyramid/__init__.py_tmpl +++ b/edbob/scaffolds/edbob/+package+/pyramid/__init__.py_tmpl @@ -7,10 +7,8 @@ import os.path from pyramid.config import Configurator -from pyramid.authentication import SessionAuthenticationPolicy import edbob -from edbob.pyramid.auth import EdbobAuthorizationPolicy def main(global_config, **settings): @@ -27,24 +25,18 @@ def main(global_config, **settings): # * Raise an exception if a setting is missing or invalid. # * Convert values from strings to their intended type. - settings['mako.directories'] = [ - '{{package}}.pyramid:templates', - 'edbob.pyramid:templates', - ] + settings.setdefault('mako.directories', [ + '{{package}}.pyramid:templates', + 'edbob.pyramid:templates', + ]) + + # Make two attempts when "retryable" errors happen during transactions. + settings.setdefault('tm.attempts', 2) config = Configurator(settings=settings) # Configure edbob edbob.init('{{package}}', os.path.abspath(settings['edbob.config'])) - - # Configure session - config.include('pyramid_beaker') - - # Configure auth - config.set_authentication_policy(SessionAuthenticationPolicy()) - config.set_authorization_policy(EdbobAuthorizationPolicy()) - - # Include "core" stuff provided by edbob. config.include('edbob.pyramid') # Additional config is defined elsewhere within {{project}}. This includes @@ -53,4 +45,8 @@ def main(global_config, **settings): config.include('{{package}}.pyramid.subscribers') config.include('{{package}}.pyramid.views') + # Consider PostgreSQL server restart errors to be "retryable." + config.add_tween('edbob.pyramid.tweens.sqlerror_tween_factory', + under='pyramid_tm.tm_tween_factory') + return config.make_wsgi_app() From 7928461e08905146a9e5d8c13238ef5ece64f864 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 21 Nov 2012 10:05:01 -0800 Subject: [PATCH 07/35] add PHONE_TYPE enum --- edbob/db/extensions/contact/enum.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/edbob/db/extensions/contact/enum.py b/edbob/db/extensions/contact/enum.py index 9972273..bf09e97 100644 --- a/edbob/db/extensions/contact/enum.py +++ b/edbob/db/extensions/contact/enum.py @@ -38,3 +38,14 @@ EMAIL_PREFERENCE = { EMAIL_PREFERENCE_HTML : "HTML", EMAIL_PREFERENCE_MOBILE : "Mobile", } + + +PHONE_TYPE_HOME = 'home' +PHONE_TYPE_MOBILE = 'mobile' +PHONE_TYPE_OTHER = 'other' + +PHONE_TYPE = { + PHONE_TYPE_HOME : "Home", + PHONE_TYPE_MOBILE : "Mobile", + PHONE_TYPE_OTHER : "Other", + } From a1d22df20c4844c56b844fbcc96b47ae2cf590c8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 21 Nov 2012 10:05:24 -0800 Subject: [PATCH 08/35] add some __unicode__() methods --- edbob/db/extensions/auth/model.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/edbob/db/extensions/auth/model.py b/edbob/db/extensions/auth/model.py index 5b5f122..a3f36f0 100644 --- a/edbob/db/extensions/auth/model.py +++ b/edbob/db/extensions/auth/model.py @@ -53,8 +53,8 @@ class Permission(Base): def __repr__(self): return "" % (self.role, self.permission) - def __str__(self): - return str(self.permission or '') + def __unicode__(self): + return unicode(self.permission or '') class UserRole(Base): @@ -99,8 +99,8 @@ class Role(Base): def __repr__(self): return "" % self.name - def __str__(self): - return str(self.name or '') + def __unicode__(self): + return unicode(self.name or '') class User(Base): @@ -126,15 +126,16 @@ class User(Base): def __repr__(self): return "" % self.username - def __str__(self): - return str(self.username or '') + def __unicode__(self): + return unicode(self.username or '') @property def display_name(self): """ - Returns the user's ``person.display_name``, if present, otherwise the - ``username``. + Returns :attr:`Person.display_name` if present; otherwise returns + :attr:`username`. """ + if self.person and self.person.display_name: return self.person.display_name return self.username From d8744f3958dcab474a7dd7d1e1d3a99c43f99e8c Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 26 Nov 2012 11:15:59 -0800 Subject: [PATCH 09/35] move contact enum to core --- edbob/__init__.py | 1 + edbob/{db/extensions/contact => }/enum.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename edbob/{db/extensions/contact => }/enum.py (96%) diff --git a/edbob/__init__.py b/edbob/__init__.py index 8ab239f..e1574df 100644 --- a/edbob/__init__.py +++ b/edbob/__init__.py @@ -28,6 +28,7 @@ from edbob._version import __version__ +from edbob.enum import * from edbob.core import * from edbob.time import * from edbob.files import * diff --git a/edbob/db/extensions/contact/enum.py b/edbob/enum.py similarity index 96% rename from edbob/db/extensions/contact/enum.py rename to edbob/enum.py index bf09e97..958dd7e 100644 --- a/edbob/db/extensions/contact/enum.py +++ b/edbob/enum.py @@ -23,7 +23,7 @@ ################################################################################ """ -``edbob.db.extensions.contact.enum`` -- Enumerations +``edbob.enum`` -- Enumerations """ From 64ae8e9136d830ffc36702d2009c21007e05a4f8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 27 Nov 2012 11:27:31 -0800 Subject: [PATCH 10/35] fix css in progress template --- edbob/pyramid/templates/progress.mako | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/edbob/pyramid/templates/progress.mako b/edbob/pyramid/templates/progress.mako index 7626e46..ea83598 100644 --- a/edbob/pyramid/templates/progress.mako +++ b/edbob/pyramid/templates/progress.mako @@ -5,7 +5,8 @@ Working... ${h.javascript_link(request.static_url('edbob.pyramid:static/js/jquery.js'))} ${h.javascript_link(request.static_url('edbob.pyramid:static/js/edbob.js'))} - ${h.stylesheet_link(request.static_url('edbob.pyramid:static/css/edbob.css'))} + ${h.stylesheet_link(request.static_url('edbob.pyramid:static/css/base.css'))} + ${h.stylesheet_link(request.static_url('edbob.pyramid:static/css/layout.css'))}