コード例 #1
0
ファイル: app.py プロジェクト: linuxsoftware/djoser
def viewRoot(root, request):
    return {
        'viewUsers': view_execution_permitted(root['users'], request),
        'viewContacts': view_execution_permitted(root['contacts'], request),
        'viewProjects': view_execution_permitted(root['projects'], request),
        'currentUser': request.user
    }
コード例 #2
0
ファイル: contacts.py プロジェクト: linuxsoftware/djoser
def editContact(contact, request):
    contacts = request.root['contacts']
    # or contact.__parent__ ?
    # or find_interface(Users) ?
    form = ContactForm(obj=contact)
    btns = ContactBtns()
    selection = request.session.get('selectedContacts', [])
    myPos = None
    btns.prevBtn.flags.disabled = True
    btns.nextBtn.flags.disabled = True
    try:
        myPos = selection.index(contact.key)
        if myPos > 0:
            btns.prevBtn.flags.disabled = False
        if myPos < len(selection) - 1:
            btns.nextBtn.flags.disabled = False
    except ValueError:
        pass
    referer = request.get('HTTP_REFERER', request.url)
    users    = request.root['users']
    projects = request.root['projects']
    retval = {'form':         form,
              'btns':         btns,
              'came_from':    referer,
              'viewUsers'    : view_execution_permitted(users,    request),
              'viewContacts' : True,
              'viewProjects' : view_execution_permitted(projects, request),
              'currentUser':  request.user}


    if request.method == 'POST':
        if 'came_from' in request.params:
            retval['came_from'] = request.params['came_from']
        form.process(request.POST)
        btns.process(request.POST)
        if btns.cancelBtn.data:
            # go back if you can
            url = request.params.get('came_from', 
                                     request.resource_url(contact).rstrip('/'))
            return HTTPFound(location = url)
        elif btns.okBtn.data:
            request.session['selectedContacts'] = []
            url = request.resource_url(contact).rstrip('/')
        elif btns.prevBtn.data and not btns.prevBtn.flags.disabled:
            selected = selection[myPos - 1]
            url = request.resource_url(contacts, selected, "@@edit")
        elif btns.nextBtn.data and not btns.nextBtn.flags.disabled:
            selected = selection[myPos + 1]
            url = request.resource_url(contacts, selected, "@@edit")
        else:
            return HTTPNotImplemented()

        if not form.validate():
            return retval
        # Update an existing Contact
        form.populate_obj(contact)
        contacts.reindex(contact)
        return HTTPFound(location = url)

    return retval
コード例 #3
0
ファイル: util.py プロジェクト: tomster/Kotti
 def _find_edit_view(self, item):
     view_name = self.request.view_name
     if not view_execution_permitted(item, self.request, view_name):
         view_name = u'edit' # XXX testme
     if not view_execution_permitted(item, self.request, view_name):
         view_name = u'' # XXX testme
     return view_name
コード例 #4
0
ファイル: contacts.py プロジェクト: linuxsoftware/djoser
def addContact(contacts, request):
    form = ContactForm()
    btns = ContactBtns()
    btns.prevBtn.flags.disabled = True
    btns.nextBtn.flags.disabled = False  # TODO support add-another
    users    = request.root['users']
    projects = request.root['projects']
    retval = {'form':         form,
              'btns':         btns,
              'viewUsers'    : view_execution_permitted(users,    request),
              'viewContacts' : True,
              'viewProjects' : view_execution_permitted(projects, request),
              'currentUser':  request.user}
    if request.method == 'POST':
        form.process(request.POST)
        btns.process(request.POST)
        if btns.cancelBtn.data:
            url = request.resource_url(contacts).rstrip('/')
            return HTTPFound(location = url)
        elif btns.okBtn.data:
            if not form.validate():
                return retval
            # Create a new Contact
            contact = Contact(form.name.data, contacts)
            form.populate_obj(contact)
            contacts.reindex(contact)
            url = request.resource_url(contact).rstrip('/')
            return HTTPFound(location = url)
        else:
            return HTTPNotImplemented()

    return retval
コード例 #5
0
def editUser(user, request):
    users = request.root['users']
    # or user.__parent__ ?
    form = UserForm(obj=user)
    btns = UserBtns()
    selection = request.session.get('selectedUsers', [])
    myPos = None
    btns.prevBtn.flags.disabled = True
    btns.nextBtn.flags.disabled = True
    try:
        myPos = selection.index(user.key)
        if myPos > 0:
            btns.prevBtn.flags.disabled = False
        if myPos < len(selection) - 1:
            btns.nextBtn.flags.disabled = False
    except ValueError:
        pass
    contacts = request.root['contacts']
    projects = request.root['projects']
    retval = {
        'form': form,
        'btns': btns,
        'viewUsers': True,
        'viewContacts': view_execution_permitted(contacts, request),
        'viewProjects': view_execution_permitted(projects, request),
        'currentUser': request.user
    }

    if request.method == 'POST':
        form.process(request.POST)
        btns.process(request.POST)
        if btns.cancelBtn.data:
            del request.session['linkFromUser']
            url = request.resource_url(user).rstrip('/')
            return HTTPFound(location=url)
        elif btns.okBtn.data:
            request.session['selectedUsers'] = []
            url = request.resource_url(user).rstrip('/')
        elif btns.prevBtn.data and not btns.prevBtn.flags.disabled:
            selected = selection[myPos - 1]
            url = request.resource_url(users, selected, "@@edit")
        elif btns.nextBtn.data and not btns.nextBtn.flags.disabled:
            selected = selection[myPos + 1]
            url = request.resource_url(users, selected, "@@edit")
        elif btns.linkBtn.data:
            request.session['linkFromUser'] = user.key
            url = request.resource_url(contacts, "@@link")
            return HTTPFound(location=url)
        else:
            return HTTPNotImplemented()

        if not form.validate():
            return retval
        # Update an existing User
        form.populate_obj(user)
        users.reindex(user)
        return HTTPFound(location=url)

    return retval
コード例 #6
0
ファイル: contacts.py プロジェクト: linuxsoftware/djoser
def viewContact(contact, request):
    form = ContactForm(obj=contact)
    users    = request.root['users']
    projects = request.root['projects']
    return {'form':        form,
            'viewUsers'    : view_execution_permitted(users,    request),
            'viewContacts' : True,
            'viewProjects' : view_execution_permitted(projects, request),
            'currentUser': request.user}
コード例 #7
0
ファイル: security.py プロジェクト: umeboshi2/Kotti
def view_permitted(context: object,
                   request: 'Request',
                   name: Optional[str] = '',
                   method: Optional[str] = 'GET') -> PermitsResult:
    with authz_context(context, request):
        with request_method(request, method):
            return view_execution_permitted(context, request, name)
コード例 #8
0
ファイル: security.py プロジェクト: castaf/Kotti
def view_permitted(context: object,
                   request: 'Request',
                   name: Optional[str] = '',
                   method: Optional[str] = 'GET') -> PermitsResult:
    with authz_context(context, request):
        with request_method(request, method):
            return view_execution_permitted(context, request, name)
コード例 #9
0
ファイル: resources.py プロジェクト: tomster/Kotti
 def addable(self, context, request):
     """Return True if the type described in 'self' may be added to
     'context'.
     """
     if view_execution_permitted(context, request, self.add_view):
         return context.type_info.name in self.addable_to
     else:
         return False # XXX testme
コード例 #10
0
ファイル: projects.py プロジェクト: linuxsoftware/djoser
def viewProjects(projects, request):
    users    = request.root['users']
    contacts = request.root['contacts']
    rows = []
    if request.method == 'POST': 
        pass
    else:
        form = ProjectForm()
        for name, project in projects.items():
            form.process(obj=project)
            rows.append([name, ]+[field.data for field in form])
        root = request.root
    return {'rows':        rows,
            'viewUsers'    : view_execution_permitted(users,    request),
            'viewContacts' : view_execution_permitted(contacts, request),
            'viewProjects' : True,
            'currentUser'  : request.user}
コード例 #11
0
ファイル: test_testing.py プロジェクト: SMFOSS/pyramid
 def test_registerView_with_permission_denying2(self):
     from pyramid import testing
     from pyramid.security import view_execution_permitted
     def view(context, request):
         """ """
     view = testing.registerView('moo.html', view=view, permission='bar')
     testing.registerDummySecurityPolicy(permissive=False)
     import types
     self.failUnless(isinstance(view, types.FunctionType))
     result = view_execution_permitted(None, None, 'moo.html')
     self.assertEqual(result, False)
コード例 #12
0
 def test_registerView_with_permission_denying2(self):
     from pyramid import testing
     from pyramid.security import view_execution_permitted
     def view(context, request):
         """ """
     view = testing.registerView('moo.html', view=view, permission='bar')
     testing.registerDummySecurityPolicy(permissive=False)
     import types
     self.assertTrue(isinstance(view, types.FunctionType))
     result = view_execution_permitted(None, None, 'moo.html')
     self.assertEqual(result, False)
コード例 #13
0
def viewProjects(projects, request):
    users = request.root['users']
    contacts = request.root['contacts']
    rows = []
    if request.method == 'POST':
        pass
    else:
        form = ProjectForm()
        for name, project in projects.items():
            form.process(obj=project)
            rows.append([
                name,
            ] + [field.data for field in form])
        root = request.root
    return {
        'rows': rows,
        'viewUsers': view_execution_permitted(users, request),
        'viewContacts': view_execution_permitted(contacts, request),
        'viewProjects': True,
        'currentUser': request.user
    }
コード例 #14
0
ファイル: util.py プロジェクト: tomster/Kotti
 def edit_links(self):
     links = []
     for name in self.context.type_info.edit_views:
         if not view_execution_permitted(self.context, self.request, name):
             continue # XXX testme
         url = resource_url(self.context, self.request, name)
         links.append(dict(
             url=url,
             name=name,
             selected=self.request.url.startswith(url),
             ))
     return links
コード例 #15
0
def addUser(users, request):
    form = UserForm()
    btns = UserBtns()
    contacts = request.root['contacts']
    projects = request.root['projects']
    retval = {
        'form': form,
        'btns': btns,
        'viewUsers': True,
        'viewContacts': view_execution_permitted(contacts, request),
        'viewProjects': view_execution_permitted(projects, request),
        'currentUser': request.user
    }
    if request.method == 'POST':
        form.process(request.POST)
        btns.process(request.POST)
        if btns.cancelBtn.data:
            url = request.resource_url(users).rstrip('/')
            return HTTPFound(location=url)
        elif btns.okBtn.data:
            if not form.validate():
                return retval
            # Create a new User
            user = User(form.name.data, users)
            form.populate_obj(user)
            users.reindex(user)
            url = request.resource_url(user).rstrip('/')
            return HTTPFound(location=url)
        elif btns.linkBtn.data:
            request.session['linkFromUser'] = user.key
            url = request.resource_url(contacts, "@@link")
            return HTTPFound(location=url)
        else:
            return HTTPNotImplemented()

    return retval
コード例 #16
0
def test(context, request):
    # should return false
    msg = 'Allow ./x? %s' % repr(
        view_execution_permitted(context, request, 'x'))
    return Response(escape(msg))
コード例 #17
0
def view_permitted(context, request, name='', method='GET'):
    with authz_context(context, request):
        with request_method(request, method):
            return view_execution_permitted(context, request, name)
コード例 #18
0
    def _callFUT(self, *arg, **kw):
        from pyramid.security import view_execution_permitted

        return view_execution_permitted(*arg, **kw)
コード例 #19
0
ファイル: app.py プロジェクト: linuxsoftware/djoser
def viewRoot(root, request):
    return {'viewUsers'    : view_execution_permitted(root['users'],    request),
            'viewContacts' : view_execution_permitted(root['contacts'], request),
            'viewProjects' : view_execution_permitted(root['projects'], request),
            'currentUser'  : request.user}
コード例 #20
0
ファイル: security.py プロジェクト: djpnewton/Kotti
def view_permitted(context, request, name=''):
    try:
        request.environ['authz_context'] = context
        return view_execution_permitted(context, request, name)
    finally:
        del request.environ['authz_context']
コード例 #21
0
ファイル: edit.py プロジェクト: tomster/Kotti
def move_node(context, request):
    P = request.POST
    session = DBSession()
    
    if 'copy' in P:
        request.session['kotti.paste'] = (context.id, 'copy')
        request.session.flash(u'%s copied.' % context.title, 'success')
        return HTTPFound(location=request.url)

    if 'cut' in P:
        request.session['kotti.paste'] = (context.id, 'cut')
        request.session.flash(u'%s cut.' % context.title, 'success')
        return HTTPFound(location=request.url)

    if 'paste' in P:
        id, action = request.session['kotti.paste']
        item = session.query(Node).get(id)
        if action == 'cut':
            if not has_permission('edit', item, request):
                raise Forbidden() # XXX testme
            item.__parent__.children.remove(item)
            context.children.append(item)
            del request.session['kotti.paste']
        elif action == 'copy':
            copy = item.copy()
            name = copy.name
            if not name: # for root
                name = title_to_name(copy.title)
            while name in context.keys():
                name = disambiguate_name(name)
            copy.name = name
            context.children.append(copy)
        request.session.flash(u'%s pasted.' % item.title, 'success')
        return HTTPFound(location=request.url)

    if 'order-up' in P or 'order-down' in P:
        up, down = P.get('order-up'), P.get('order-down')
        id = int(down or up)
        if up is not None: # pragma: no cover
            mod = -1
        else:
            mod = +1

        child = session.query(Node).get(id)
        index = context.children.index(child)
        context.children.pop(index)
        context.children.insert(index+mod, child)
        request.session.flash(u'%s reordered.' % child.title, 'success')

    if 'delete' in P and 'delete-confirm' in P:
        parent = context.__parent__
        parent.children.remove(context)

        elements = []
        if view_execution_permitted(parent, request, 'edit'):
            elements.append('edit')
        location = resource_url(parent, request, *elements)
        request.session.flash(u'%s deleted.' % context.title, 'success')
        return HTTPFound(location=location)

    return {
        'api': TemplateAPIEdit(context, request),
        }
コード例 #22
0
ファイル: test_security.py プロジェクト: AdamG/pyramid
 def _callFUT(self, *arg, **kw):
     from pyramid.security import view_execution_permitted
     return view_execution_permitted(*arg, **kw)
コード例 #23
0
ファイル: security.py プロジェクト: MichaelGregory/Kotti
def view_permitted(context, request, name=''):
    with authz_context(context, request):
        return view_execution_permitted(context, request, name)
コード例 #24
0
def viewUsers(users, request):
    from pyramid.security import principals_allowed_by_permission
    print "allowed to", principals_allowed_by_permission(Users, 'view')
    btns = UsersBtns()
    if request.method == 'POST':
        btns.process(request.POST)
        selection = [
            selected[7:] for selected in request.POST
            if selected.startswith("select-")
        ]
        if btns.addBtn.data:
            url = request.resource_url(users, "@@add-user")
            return HTTPFound(location=url)
        elif selection and btns.delBtn.data:
            users.delete(selection)
        elif selection and btns.editBtn.data:
            request.session['selectedUsers'] = selection
            user = users.get(selection[0])
            if user:
                url = request.resource_url(user, "@@edit")
                return HTTPFound(location=url)
        else:
            return HTTPNotImplemented()
        url = request.resource_url(users).rstrip('/')
        return HTTPFound(location=url)

    pgParam = request.params.get("page", "1")
    current_page = int(pgParam)
    sortParam = request.params.get("sort", "")
    posCache = request.session.get('posCache', {})
    #FIXME currentIndex can't be stored in users
    if (sortParam and sortParam != users._currentIndex
            and sortParam in users._cat):
        users._currentIndex = sortParam
        current_page = 1
        posCache.clear()

    numUsers = len(users)
    idx = users.getCurrentIndex()
    slicer = CatalogFieldIndexSlicer(idx, numUsers, posCache)
    page = Page(slicer, current_page, 10)
    request.session['posCache'] = posCache
    lastPg = (numUsers + 9) / 10

    #form = UserForm()
    rows = []
    for key in page:
        user = users.get(key)
        if user is None: continue
        #form.process(obj=user)
        name = user.name
        # TODO use BooleanField?
        checkbox = '<input type="checkbox" '\
                   'name="select-%d" value="" />' % key
        groups = ", ".join(Groups[grp] for grp in user.groups)
        contact = ""
        if user.contact:
            contact = '<a href="/contacts/%d">%s</a>' % user.contact
        rows.append([checkbox, name, groups, contact])

    contacts = request.root['contacts']
    projects = request.root['projects']
    #TODO move viewUsers,viewContacts,viewProjects into the User object
    return {
        'btns':
        btns,
        'rows':
        rows,
        'numUsers':
        numUsers,
        'pager':
        '<a href="?page=1">1</a> ... ' + '<a href="?page=%d">%d</a>' %
        (lastPg, lastPg),
        'viewUsers':
        True,
        'viewContacts':
        view_execution_permitted(contacts, request),
        'viewProjects':
        view_execution_permitted(projects, request),
        'currentUser':
        request.user
    }
コード例 #25
0
ファイル: security.py プロジェクト: WenkeZhou/Kotti
def view_permitted(context, request, name='', method='GET'):
    with authz_context(context, request):
        with request_method(request, method):
            return view_execution_permitted(context, request, name)
コード例 #26
0
ファイル: tests.py プロジェクト: bluedynamics/cone.tile
    def test_secured(self, authn):
        @tile(name='protected_login', permission='login')
        class ProtectedLogin(Tile):
            def render(self):
                return u'permission login'

        @tile(name='protected_view', permission='view')
        class ProtectedView(Tile):
            def render(self):
                return u'permission view'

        @tile(name='protected_edit', permission='edit')
        class ProtectedEdit(Tile):
            def render(self):
                return u'permission edit'

        @tile(name='protected_delete', permission='delete')
        class ProtectedDelete(Tile):
            def render(self):
                return u'permission delete'

        model = Model()
        request = self.layer.new_request()

        # Define ACL for model
        model.__acl__ = [
            (Allow, 'system.Authenticated', ['view']),
            (Allow, 'role:editor', ['view', 'edit']),
            (Allow, 'role:manager', ['view', 'edit', 'delete']),
            (Allow, Everyone, ['login']),
            (Deny, Everyone, ALL_PERMISSIONS),
        ]

        # No authenticated user
        authn.unauthenticated_userid = lambda *args: None

        # Login permission protected tile can be rendered
        self.assertEqual(
            render_tile(model, request, 'protected_login'),
            u'permission login'
        )

        # View permission protected tile rendering fails for anonymous
        err = self.expectError(
            HTTPForbidden,
            render_tile,
            model,
            request,
            'protected_view'
        )
        self.assertTrue(str(err).find('failed permission check') > -1)

        rule = view_execution_permitted(model, request, name='protected_view')
        self.assertTrue(isinstance(rule, ACLDenied))

        # Set authenticated to 'max'
        authn.unauthenticated_userid = lambda *args: 'max'

        # Authenticated users are allowed to view tiles protected by view
        # permission
        self.assertEqual(
            render_tile(model, request, 'protected_view'),
            u'permission view'
        )

        # Edit permission protected tile rendering fails for authenticated
        err = self.expectError(
            HTTPForbidden,
            render_tile,
            model,
            request,
            'protected_edit'
        )
        self.assertTrue(str(err).find('failed permission check') > -1)

        # Set authenticated to 'editor_user'
        authn.unauthenticated_userid = lambda *args: 'editor_user'

        # Editor is allowed to render edit permission protected tiles
        self.assertEqual(
            render_tile(model, request, 'protected_edit'),
            u'permission edit'
        )

        # Delete permission protected tile rendering fails for editor
        err = self.expectError(
            HTTPForbidden,
            render_tile,
            model,
            request,
            'protected_delete'
        )
        self.assertTrue(str(err).find('failed permission check') > -1)

        # Set User to 'admin_user'
        authn.unauthenticated_userid = lambda *args: 'admin_user'

        # Admin users are allowed to render delete permission protected tiles
        # and others
        self.assertEqual(
            render_tile(model, request, 'protected_delete'),
            u'permission delete'
        )
        self.assertEqual(
            render_tile(model, request, 'protected_edit'),
            u'permission edit'
        )
        self.assertEqual(
            render_tile(model, request, 'protected_view'),
            u'permission view'
        )
        self.assertEqual(
            render_tile(model, request, 'protected_login'),
            u'permission login'
        )

        # Override secured tile
        @tile(name='protected_delete', permission='delete')
        class ProtectedDeleteOverride(Tile):
            def render(self):
                return u'permission delete override'

        self.assertEqual(
            render_tile(model, request, 'protected_delete'),
            u'permission delete override'
        )

        # If tile is registered non-strict, render_tile returns empty string
        @tile(name='protected_unstrict', permission='delete', strict=False)
        class ProtectedUnstrict(Tile):
            pass

        authn.unauthenticated_userid = lambda *args: None

        self.layer.logger.clear()

        self.assertEqual(render_tile(model, request, 'protected_unstrict'), u'')

        self.checkOutput("""
        Unauthorized: tile <cone.tile.tests...ProtectedUnstrict object at ...>
        failed permission check
        """, self.layer.logger.messages[0])

        self.layer.logger.clear()

        # If an error occours in tile, do not swallow error
        @tile(name='raisingtile', permission='login')
        class RaisingTile(Tile):
            def render(self):
                raise Exception(u'Tile is not willing to perform')

        err = self.expectError(
            Exception,
            render_tile,
            model,
            request,
            'raisingtile'
        )
        self.assertEqual(str(err), 'Tile is not willing to perform')
コード例 #27
0
ファイル: contacts.py プロジェクト: linuxsoftware/djoser
def viewContacts(contacts, request):
    btns = ContactsBtns()
    if request.method == 'POST': 
        btns.process(request.POST)
        selection = [ int(selected[7:]) for selected in request.POST
                                        if selected.startswith("select-") ]
        if btns.addBtn.data:
            url = request.resource_url(contacts, "@@add-contact")
            return HTTPFound(location = url)
        elif selection and btns.delBtn.data:
            contacts.delete(selection)
        elif selection and btns.editBtn.data:
            request.session['selectedContacts'] = selection
            contact = contacts.get(selection[0])
            if contact:
                url = request.resource_url(contact, "@@edit")
                return HTTPFound(location = url)
        else:
            return HTTPNotImplemented()
        url = request.resource_url(contacts).rstrip('/')
        return HTTPFound(location = url)

    form = ContactForm()
    headings = [(field.name, field.label.text) for field in form]
    rows = []

    pgParam = request.params.get("page", "1")
    current_page = int(pgParam)
    sortParam = request.params.get("sort", "")
    posCache = request.session.get('posCache', {})
    #FIXME currentIndex can't be stored in contacts
    if (sortParam and sortParam != contacts._currentIndex and 
        sortParam in contacts._cat):
        contacts._currentIndex = sortParam
        current_page = 1
        posCache.clear()
        
    numContacts = len(contacts)
    idx = contacts.getCurrentIndex()
    slicer = CatalogFieldIndexSlicer(idx, numContacts, posCache)
    page = Page(slicer, current_page, 10)
    request.session['posCache'] = posCache
    lastPg = (numContacts + 9) / 10

    for key in page:
        contact = contacts.get(key)
        if contact is None: continue
        form.process(obj=contact)
        # TODO use WTForms.BooleanField?
        checkbox = '<input type="checkbox" '\
                   'name="select-%d" value="" />'% key
        rows.append([checkbox, ]+[field.data for field in form])
    
    users    = request.root['users']
    projects = request.root['projects']
    return {'btns':        btns,
            'headings':    headings,
            'rows':        rows,
            'numContacts': numContacts,
            'pager':       '<a href="?page=1">1</a> ... '+
                           '<a href="?page=%d">%d</a>' % (lastPg, lastPg),
            'viewUsers'    : view_execution_permitted(users,    request),
            'viewContacts' : True,
            'viewProjects' : view_execution_permitted(projects, request),
            'currentUser': request.user}
コード例 #28
0
ファイル: __init__.py プロジェクト: Pylons/pyramid
def test(context, request):
    # should return false
    msg = 'Allow ./x? %s' % repr(
        view_execution_permitted(context, request, 'x')
    )
    return Response(escape(msg))
コード例 #29
0
ファイル: security.py プロジェクト: dnouri/Kotti
def view_permitted(context, request, name=''):
    with authz_context(context, request):
        return view_execution_permitted(context, request, name)
コード例 #30
0
ファイル: contacts.py プロジェクト: linuxsoftware/djoser
def linkContact(contacts, request):
    btns = ContactBtns()
    users    = request.root['users']
    projects = request.root['projects']

    if request.method == 'POST': 
        btns.process(request.POST)
        selection = [ selected[7:] for selected in request.POST
                                   if selected.startswith("select-") ]
        key = request.session['linkFromUser']
        user = users.get(key)
        if user is None:
            return HTTPNotImplemented()
        url = request.resource_url(user)
        if btns.okBtn.data:
            if selection:
                contact = contacts.get(selection[0])
                if not contact:
                    return HTTPNotImplemented()
                user.contact = (contact.key, contact.name)
            else:
                user.contact = None
            return HTTPFound(location = url)
        elif btns.cancelBtn.data:
            return HTTPFound(location = url)
        else:
            return HTTPNotImplemented()

    #TODO this is all common and shoule be reuseable
    form = ContactForm()
    headings = [(field.name, field.label.text) for field in form]
    rows = []

    pgParam = request.params.get("page", "1")
    current_page = int(pgParam)
    sortParam = request.params.get("sort", "")
    posCache = request.session.get('posCache', {})
    #FIXME currentIndex can't be stored in contacts
    if (sortParam and sortParam != contacts._currentIndex and 
        sortParam in contacts._cat):
        contacts._currentIndex = sortParam
        current_page = 1
        posCache.clear()
        
    numContacts = len(contacts)
    idx = contacts.getCurrentIndex()
    slicer = CatalogFieldIndexSlicer(idx, numContacts, posCache)
    page = Page(slicer, current_page, 10)
    #import pdb; pdb.set_trace()
    request.session['posCache'] = posCache
    lastPg = (numContacts + 9) / 10

    for key in page:
        contact = contacts.get(key)
        if contact is None: continue
        form.process(obj=contact)
        # TODO use WTForms.BooleanField?
        checkbox = '<input type="checkbox" '\
                   'name="select-%d" value="" />'% key
        rows.append([checkbox, ]+[field.data for field in form])
    
    return {'btns':        btns,
            'headings':    headings,
            'rows':        rows,
            'numContacts': numContacts,
            'pager':       '<a href="?page=1">1</a> ... '+
                           '<a href="?page=%d">%d</a>' % (lastPg, lastPg),
            'viewUsers'    : view_execution_permitted(users,    request),
            'viewContacts' : True,
            'viewProjects' : view_execution_permitted(projects, request),
            'currentUser': request.user}